API Error Responses
This guide provides comprehensive information about error responses from the Grizzly API, including HTTP status codes, error formats, and common error scenarios.
Error Response Format
All API error responses follow a consistent JSON format:
{
"message": "Description of what went wrong"
}
Key Points:
- Errors always include a
messagefield describing the error - The HTTP status code indicates the error category
- Error messages are designed to be actionable and developer-friendly
HTTP Status Codes
The Grizzly API uses standard HTTP status codes to indicate success or failure.
2xx Success
| Code | Status | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource successfully created |
4xx Client Errors
| Code | Status | Description | Common Causes |
|---|---|---|---|
| 400 | Bad Request | Invalid request format or parameters | Missing required fields, invalid JSON, malformed data |
| 401 | Unauthorized | Authentication failed | Invalid or missing API key, expired token |
| 403 | Forbidden | Insufficient permissions or quota exceeded | Missing required permission flags, resource limit reached |
| 404 | Not Found | Resource doesn't exist | KeyRing not found, User doesn't exist, invalid ID |
| 422 | Unprocessable Entity | Request is valid but contains semantic errors | Asset validation failed |
5xx Server Errors
| Code | Status | Description |
|---|---|---|
| 500 | Internal Server Error | Unexpected server error |
Common Error Scenarios
Authentication Errors (401)
Invalid API Key
Request:
curl -X GET https://your-tenant.grizzlycbg.io/ks/keyrings \
-H "Authorization: Bearer invalid-api-key"
Response:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"message": "Unauthorized"
}
Common Causes:
- API key is incorrect or doesn't exist
- API key has been disabled
- API key belongs to a different tenant
- Missing
Bearerprefix in Authorization header
Solutions:
- Verify API key is correct and active
- Check that API key is properly formatted:
Bearer apikey-xxx - Ensure API key belongs to the correct tenant
Missing Authorization Header
Request:
curl -X GET https://your-tenant.grizzlycbg.io/ks/keyrings
Response:
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"message": "Unauthorized"
}
Solution: Always include the Authorization header with Bearer token:
curl -X GET https://your-tenant.grizzlycbg.io/ks/keyrings \
-H "Authorization: Bearer your-api-key"
Permission Errors (403)
Insufficient Permissions
Request:
curl -X POST https://your-tenant.grizzlycbg.io/ks/rotate \
-H "Authorization: Bearer apikey-xxx" \
-H "Content-Type: application/json" \
-d '{"ringname": "Finance"}'
Response:
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"message": "Insufficient permissions to rotate keys for KeyRing 'Finance'"
}
Explanation: The API key doesn't have the required permission flag for this operation.
Required Permission:
- keyring.Finance.rotate or keyring.*.rotate
Solution: Update the API key to include the required permission flag.
Bad Request Errors (400)
Missing Required Field
Request:
curl -X POST https://your-tenant.grizzlycbg.io/ks/keyrings \
-H "Authorization: Bearer apikey-xxx" \
-H "Content-Type: application/json" \
-d '{}'
Response:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"message": "Missing required field: name"
}
Explanation:
The name field is required when creating a KeyRing.
Correct Request:
curl -X POST https://your-tenant.grizzlycbg.io/ks/keyrings \
-H "Authorization: Bearer apikey-xxx" \
-H "Content-Type: application/json" \
-d '{"name": "Finance"}'
Invalid JSON
Request:
curl -X POST https://your-tenant.grizzlycbg.io/ks/keyrings \
-H "Authorization: Bearer apikey-xxx" \
-H "Content-Type: application/json" \
-d '{"name": "Finance"'
Response:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"message": "Invalid JSON in request body"
}
Explanation: The JSON payload is malformed (missing closing brace).
Solution: Ensure JSON is properly formatted and valid.
Missing Content-Type Header
Request:
curl -X POST https://your-tenant.grizzlycbg.io/ks/keyrings \
-H "Authorization: Bearer apikey-xxx" \
-d '{"name": "Finance"}'
Response:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"message": "Content-Type must be application/json"
}
Solution:
Always include Content-Type: application/json header for POST/PUT requests.
Password Validation Failed
Request:
curl -X POST https://your-tenant.grizzlycbg.io/auth/user/invite-complete \
-H "Content-Type: application/json" \
-d '{
"token": "invite-token-123",
"password": "weak"
}'
Response:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"state": "malformed-password",
"results": {
"isValid": false,
"details": {
"minLength": 8,
"newPasswordLength": 4,
"minUppercase": 1,
"newPasswordUppercase": 0,
"minLowercase": 1,
"newPasswordLowercase": 4,
"minSpecialChar": 1,
"newPasswordSpecialChar": 0,
"minNumber": 1,
"newPasswordNumber": 0
}
}
}
Explanation: Password doesn't meet complexity requirements.
Password Requirements:
- Minimum length: 8 characters
- At least 1 uppercase letter
- At least 1 lowercase letter
- At least 1 number
- At least 1 special character
Not Found Errors (404)
KeyRing Not Found
Request:
curl -X GET https://your-tenant.grizzlycbg.io/ks/keyrings/NonExistentRing \
-H "Authorization: Bearer apikey-xxx"
Response:
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"message": "KeyRing 'NonExistentRing' not found"
}
Common Causes:
- KeyRing name is misspelled
- KeyRing doesn't exist
- Using wrong case (though names are case-insensitive)
Solution:
- List all KeyRings:
GET /ks/keyrings - Verify KeyRing name spelling
- Create the KeyRing if it doesn't exist
User Not Found
Request:
curl -X GET https://your-tenant.grizzlycbg.io/auth/users/nonexistent \
-H "Authorization: Bearer apikey-xxx"
Response:
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"message": "User 'nonexistent' not found"
}
Solution:
- Verify username is correct
- Check if using email instead of username (use
?email=truequery parameter) - List all users:
GET /auth/users
Account Not Found
Request:
curl -X GET https://your-tenant.grizzlycbg.io/auth/accounts/invalid-id \
-H "Authorization: Bearer apikey-xxx"
Response:
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"message": "Account not found"
}
Solution:
- Verify Account ID is correct
- Use Account UID if applicable:
GET /auth/accounts/{id}?uid=true
Validation Errors (422)
Asset Validation Failed
Request:
curl -X POST https://your-tenant.grizzlycbg.io/ks/decrypt \
-H "Authorization: Bearer apikey-xxx" \
-H "Content-Type: application/json" \
-d '{
"hash": "invalid-hash",
"header": "tampered-header"
}'
Response:
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"ringId": "ring-123",
"ringname": "finance",
"key": {
"type": "aes256",
"id": "key-456"
},
"asset": {
"valid": false,
"id": "asset-789",
"message": "Asset signature verification failed",
"data": {}
}
}
Explanation: The encrypted data's asset metadata has been tampered with or is corrupted. The cryptographic signature doesn't match.
Common Causes:
- Data was modified after encryption
- Header was separated from payload
- Encoding issues during storage/transmission
Solution:
- Ensure encrypted data is stored and retrieved without modification
- Verify no middleware is altering the data
- Check for encoding/decoding issues (base64, etc.)
Server Errors (500)
Internal Server Error
Request:
curl -X POST https://your-tenant.grizzlycbg.io/ks/rotate \
-H "Authorization: Bearer apikey-xxx" \
-H "Content-Type: application/json" \
-d '{"ringname": "Finance"}'
Response:
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{
"message": "An unexpected error occurred while rotating the key"
}
Explanation: An unexpected server-side error occurred.
What to Do:
- Retry the request after a brief delay
- Check if the issue persists
- Review request parameters for potential issues
- Contact support if the error continues
- Provide the request details and timestamp for investigation
Resource-Specific Errors
KeyRings
Duplicate KeyRing Name (400)
{
"message": "The KeyRing 'Finance' already exists. KeyRings must have unique names."
}
KeyRing Deletion Failed (400)
{
"message": "Cannot delete KeyRing. Active API keys are still using this KeyRing."
}
API Keys
API Key Not Found (404)
{
"message": "API Key not found"
}
Account Not Found When Creating API Key (404)
{
"message": "Account not found. Cannot create API key."
}
Users
Duplicate Username (400)
{
"message": "User with username 'john.doe' already exists"
}
Duplicate Email (400)
{
"message": "User with email 'john@example.com' already exists",
"errors": {
"email": "Email already registered"
}
}
Invitations
Invalid Invite Token (401)
{
"state": "invalid"
}
Expired Invite Token (401)
{
"state": "expired"
}
Login
Invalid Credentials (400)
{
"message": "Invalid username or password"
}
Error Handling Best Practices
1. Always Check Status Codes
const response = await fetch('https://your-tenant.grizzlycbg.io/ks/keyrings', {
headers: {
'Authorization': `Bearer ${apiKey}`
}
})
if (!response.ok) {
const error = await response.json()
console.error(`API Error (${response.status}):`, error.message)
// Handle specific error codes
if (response.status === 401) {
// Refresh authentication
} else if (response.status === 403) {
// Check permissions
} else if (response.status === 404) {
// Resource doesn't exist
}
}
2. Implement Retry Logic
async function apiCallWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options)
if (response.ok) {
return await response.json()
}
// Don't retry client errors (4xx)
if (response.status >= 400 && response.status < 500) {
const error = await response.json()
throw new Error(error.message)
}
// Retry server errors (5xx)
if (response.status >= 500 && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000 // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay))
continue
}
const error = await response.json()
throw new Error(error.message)
} catch (error) {
if (attempt === maxRetries) {
throw error
}
}
}
}
3. Parse Error Messages
async function createKeyRing(name) {
try {
const response = await fetch('https://your-tenant.grizzlycbg.io/ks/keyrings', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name })
})
if (!response.ok) {
const error = await response.json()
// Handle specific error scenarios
if (error.message.includes('already exists')) {
console.log('KeyRing already exists, fetching existing one...')
return await getKeyRing(name)
}
if (error.message.includes('maximum number')) {
console.error('Plan limit reached:', error.message)
// Prompt user to upgrade plan
return null
}
throw new Error(error.message)
}
return await response.json()
} catch (error) {
console.error('Failed to create KeyRing:', error.message)
throw error
}
}
4. Log Errors for Debugging
async function makeApiCall(url, options) {
const requestId = crypto.randomUUID()
console.log(`[${requestId}] Request:`, {
url,
method: options.method,
timestamp: new Date().toISOString()
})
try {
const response = await fetch(url, options)
if (!response.ok) {
const error = await response.json()
console.error(`[${requestId}] Error Response:`, {
status: response.status,
message: error.message,
timestamp: new Date().toISOString()
})
throw new Error(error.message)
}
console.log(`[${requestId}] Success:`, {
status: response.status,
timestamp: new Date().toISOString()
})
return await response.json()
} catch (error) {
console.error(`[${requestId}] Request Failed:`, error.message)
throw error
}
}
Next Steps
- Pagination - Handling paginated responses
- Troubleshooting - Common issues and solutions
- API Specification - Complete API reference