Error Handling & Troubleshooting
This guide helps you diagnose and resolve common issues when working with the Grizzly platform.
Common Error Patterns
Authentication Errors
Problem: Requests fail with authentication errors (401 Unauthorized).
Symptoms:
- Authentication failed or Invalid API key
- HTTP 401 status codes
- SDK throws authentication-related errors
Solutions:
Verify API Key
Ensure your API key is valid and has not been disabled:# Test with curl
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://your-tenant.grizzlycbg.io/auth/apikey
# Expected: 200 OK with API key details
# If 401: API key is invalid or disabled
Check API Key Format
API keys must be passed as Bearer tokens in the Authorization header:// Correct
headers: {
'Authorization': 'Bearer apikey-123456-1234567890'
}
// Incorrect (missing "Bearer")
headers: {
'Authorization': 'apikey-123456-1234567890'
}
Permission Errors
Problem: Requests fail with permission denied (403 Forbidden).
Symptoms:
- Insufficient permissions or Access denied
- HTTP 403 status codes
- Operations fail even with valid authentication
Solutions:
Verify API Key Flags
Check that your API key has the required permissions (flags):# Get API key details
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://your-tenant.grizzlycbg.io/auth/apikey
Check KeyRing-Specific Permissions
If you can access some KeyRings but not others, verify KeyRing-specific flags:// API key with limited access
flags: {
"keyring.Finance.encrypt": true, // Can encrypt with Finance
"keyring.Finance.decrypt": true, // Can decrypt with Finance
"keyring.HR.encrypt": false // Cannot use HR KeyRing
}
KeyRing Not Found
Problem: Operations fail because the KeyRing doesn't exist.
Symptoms:
- KeyRing not found errors
- HTTP 404 status codes when accessing KeyRings
- SDK returns undefined when getting a KeyRing
Solutions:
Verify KeyRing Exists
**Using SDK:**const ring = await client.ring.get('Finance')
if (!ring) {
console.log('KeyRing does not exist')
// Create it
const newRing = await client.ring.create('Finance')
}
# List all KeyRings
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://your-tenant.grizzlycbg.io/ks/rings
Check KeyRing Name Case Sensitivity
KeyRing names are case-insensitive but stored in lowercase:// These all refer to the same KeyRing
await client.ring.get('Finance') // Stored as 'finance'
await client.ring.get('finance')
await client.ring.get('FINANCE')
// Display name preserves casing
const ring = await client.ring.create('Finance')
console.log(ring.name) // 'finance'
console.log(ring.displayName) // 'Finance'
Decryption Failures
Problem: Data fails to decrypt even though it was encrypted successfully.
Symptoms:
- Decryption failed or Invalid encrypted data
- Authentication tag verification failed
- Garbled or corrupted output
Solutions:
Verify Data Integrity
Ensure encrypted data hasn't been modified:// Encrypted data must be preserved exactly as returned
const encrypted = await ring.encrypt('data')
// ❌ Don't modify encrypted data
const modified = encrypted.substring(0, encrypted.length - 10)
// ✅ Store and retrieve encrypted data without changes
await ring.decrypt(encrypted) // Works
await ring.decrypt(modified) // Fails
Check Private Key Availability
For customer-managed keys, ensure the private key is accessible:// Decryption with customer-managed keys
const privateKey = fs.readFileSync('./private.pem', 'utf-8')
const decrypted = await client.decrypt(encrypted, privateKey)
// Common issues:
// - Private key file path incorrect
// - Private key format wrong (must be PEM-encoded PKCS8)
// - Private key doesn't match the public key used for KeyRing
Validate Header Format
The encrypted data must include a valid Grizzly header:// Encrypted data structure:
// [Header][Encrypted Payload][Auth Tag]
// If you're manually handling encrypted data:
// - Don't strip the header
// - Don't separate header from payload
// - Keep all three components together
Network and Connection Errors
Problem: Requests timeout or fail to connect to the Grizzly platform.
Symptoms:
- Connection refused or ECONNREFUSED
- Timeout or Network error
- SDK operations hang indefinitely
Solutions:
Verify Host URL
Ensure the host URL is correct and accessible:// ✅ Correct format
const client = await KeyClient.create({
host: 'https://your-tenant.grizzlycbg.io',
apikey: 'apikey-123456-1234567890'
})
// ❌ Common mistakes
host: 'http://your-tenant.grizzlycbg.io' // Wrong protocol
host: 'your-tenant.grizzlycbg.io' // Missing https://
host: 'https://your-tenant.grizzlycbg.io/' // Trailing slash
curl https://your-tenant.grizzlycbg.io/healthz
Check Firewall and Network Settings
- Ensure outbound HTTPS (port 443) is allowed - Verify no proxy is blocking connections - Check if VPN or corporate firewall restricts access - Confirm DNS resolves the hostname correctlyHandle Timeouts Gracefully
async function encryptWithRetry(data, keyring, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const ring = await client.ring.get(keyring)
return await ring.encrypt(data)
} catch (error) {
if (error.message.includes('timeout') && attempt < maxRetries) {
console.log(`Attempt ${attempt} failed, retrying...`)
await new Promise(resolve => setTimeout(resolve, 1000 * attempt))
continue
}
throw error
}
}
}
Key Rotation Issues
Problem: Key rotation fails or behaves unexpectedly.
Symptoms:
- Key rotation failed
- New encryptions still use old key
- Configuration changes don't trigger rotation
Solutions:
Verify Rotation Configuration
Check KeyRing configuration for rotation settings:const ring = await client.ring.get('Finance')
const config = await ring.getConfig()
console.log('Max encrypt count:', config.algos.aes256.maxEncryptCount)
// Rotation happens automatically when this count is reached
// Or manually with:
await ring.rotate()
Understand Rotation Behavior
**Important points:** - Rotated keys are **never deleted** - Old encrypted data **still decrypts** after rotation - Only **new encryptions** use the new key - Rotation is **irreversible**// Before rotation
const ring1 = await client.ring.get('Finance')
console.log('Active key:', ring1.activeKey.id) // key-001
const data1 = await ring1.encrypt('Old data')
// Rotate
await ring1.rotate()
// After rotation
const ring2 = await client.ring.get('Finance')
console.log('Active key:', ring2.activeKey.id) // key-002
const data2 = await ring2.encrypt('New data')
// Both decrypt successfully
await ring2.decrypt(data1) // Works (uses key-001)
await ring2.decrypt(data2) // Works (uses key-002)
SDK-Specific Errors
Module Not Found
Problem: Import errors when using the SDK.
Symptoms:
Error: Cannot find module '@grizzlycbg/grizzly-sdk'
Solutions:
Verify Installation
# Check if SDK is installed
npm list @grizzlycbg/grizzly-sdk
# If not installed
npm install @grizzlycbg/grizzly-sdk
# Verify package.json
cat package.json | grep grizzly-sdk
Check Node.js Version
The SDK requires Node.js 20 or higher:node --version # Should be v20.x.x or higher
# If using older version, update Node.js
# https://nodejs.org/
Stream Handling Errors
Problem: Errors when encrypting/decrypting streams.
Symptoms:
- Stream is not readable
- Premature close
- Incomplete file encryption/decryption
Solutions:
Handle Stream Events Properly
async function encryptFileStream(inputPath, outputPath, ring) {
const readStream = fs.createReadStream(inputPath)
const writeStream = fs.createWriteStream(outputPath)
try {
const encryptedStream = await ring.encrypt(readStream)
return new Promise((resolve, reject) => {
// Handle all stream events
encryptedStream.pipe(writeStream)
writeStream.on('finish', () => resolve())
writeStream.on('error', reject)
encryptedStream.on('error', reject)
readStream.on('error', reject)
})
} catch (error) {
// Cleanup on error
readStream.destroy()
writeStream.destroy()
throw error
}
}
API-Specific Errors
Invalid Request Body
Problem: API returns 400 Bad Request.
Symptoms:
{
"error": "Invalid request body",
"details": "Missing required field: name"
}
Solutions:
Validate Request Format
**Common issues:** - Missing required fields - Incorrect data types - Invalid JSON syntax// ❌ Incorrect
{
ringname: 'Finance', // Wrong field name
displayName: 123 // Wrong type (should be string)
}
// ✅ Correct
{
"name": "Finance",
"displayName": "Finance Team"
}
Check Content-Type Header
Ensure you're sending the correct content type:# ✅ Correct
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer apikey-xxx" \
-d '{"name":"Finance"}' \
https://your-tenant.grizzlycbg.io/asym/keyrings
# ❌ Missing Content-Type
curl -X POST \
-H "Authorization: Bearer apikey-xxx" \
-d '{"name":"Finance"}' \
https://your-tenant.grizzlycbg.io/asym/keyrings
Rate Limiting
Problem: Too many requests in a short time.
Symptoms:
- HTTP 429 (Too Many Requests)
- Rate limit exceeded errors
Solutions:
Implement Backoff Strategy
async function requestWithBackoff(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000 // Exponential backoff
console.log(`Rate limited, waiting ${delay}ms...`)
await new Promise(resolve => setTimeout(resolve, delay))
continue
}
throw error
}
}
}
// Usage
const result = await requestWithBackoff(async () => {
return await fetch('https://your-tenant.grizzlycbg.io/ks/rings', {
headers: { 'Authorization': 'Bearer apikey-xxx' }
})
})
Batch Operations
Instead of making many individual requests, batch where possible:// ❌ Many sequential requests
for (const user of users) {
await ring.encrypt(user.data)
}
// ✅ Parallel with concurrency limit
const limit = 10 // Max concurrent requests
for (let i = 0; i < users.length; i += limit) {
const batch = users.slice(i, i + limit)
await Promise.all(batch.map(user => ring.encrypt(user.data)))
}
Debugging Tips
Enable Debug Logging
SDK Debug Mode
// Set environment variable
process.env.GRIZZLY_DEBUG = 'true'
// Or enable in code
const client = await KeyClient.create({
host: 'https://your-tenant.grizzlycbg.io',
apikey: 'apikey-123456-1234567890',
debug: true // If supported
})
Test Connectivity
Health Check
Test basic connectivity to the Grizzly platform:# Health endpoint
curl https://your-tenant.grizzlycbg.io/healthz
# Expected response: {"status":"ok"}
Inspect Encrypted Data
Examine Header
Encrypted data contains a header with metadata:// Encrypted data structure (base64 encoded):
// [Version][Header Length][Header][Encrypted Payload][Auth Tag]
// To inspect (not for production):
const encrypted = await ring.encrypt('data')
const buffer = Buffer.from(encrypted, 'base64')
console.log('Total length:', buffer.length)
console.log('First 20 bytes:', buffer.slice(0, 20).toString('hex'))
Getting Help
If you're still experiencing issues:
- Check the Activity Feed - Review recent encryption/decryption operations for clues
- Verify API Key Status - Ensure API key is active and has required permissions
- Review Recent Changes - Check if KeyRing configuration or API key flags changed
- Test with Minimal Example - Isolate the issue with a simple test case
- Contact Support - Provide error messages, request IDs, and reproduction steps
Common Error Reference
| Error Message | Cause | Solution |
|---|---|---|
Authentication failed |
Invalid or disabled API key | Verify API key is correct and active |
Insufficient permissions |
Missing required flag | Update API key flags |
KeyRing not found |
KeyRing doesn't exist | Create KeyRing or check name spelling |
Decryption failed |
Corrupted data or wrong key | Verify data integrity and private key |
Invalid encrypted data |
Malformed header or payload | Ensure data not modified after encryption |
Connection refused |
Wrong host or network issue | Check host URL and network connectivity |
Timeout |
Network latency or server issue | Implement retry logic with backoff |
Rate limit exceeded |
Too many requests | Reduce request rate or implement backoff |
Invalid request body |
Missing/incorrect fields | Validate request structure |
Key rotation failed |
Permission or configuration issue | Check rotation permissions and config |