Error Handling & Troubleshooting
Complete guide to handling errors and troubleshooting issues with the B4M Completions API.
Error Format
All errors are returned as SSE error events:
{
"type": "error",
"message": "Human-readable error description"
}
Errors can occur at two stages:
- Before streaming - HTTP error status (401, 400, 429, 500, etc.)
- During streaming - SSE error event in the stream
HTTP Status Codes
| Status | Name | When It Happens |
|---|---|---|
| 200 | Success | Request accepted, SSE stream begins |
| 400 | Bad Request | Invalid request format or parameters |
| 401 | Unauthorized | Authentication failed (invalid API key/JWT) |
| 403 | Forbidden | Insufficient permissions or credits |
| 422 | Unprocessable Entity | Request validation failed (schema mismatch) |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server error (contact support if persistent) |
Common Errors
Authentication Errors
Error: Authentication failed
Full message: "Authentication failed. Provide a valid API key or JWT token."
HTTP Code: 401
Causes:
- Missing authentication header
- Invalid API key format
- Expired JWT token
- API key revoked
Solutions:
- Verify API key is set correctly:
echo $B4M_API_KEY # Should print your key
- Check API key format (should start with
b4m_live_orb4m_test_) - Verify header format:
X-API-Key: b4m_live_xxxxxxxxxxxx# ORAuthorization: ApiKey b4m_live_xxxxxxxxxxxx# ORAuthorization: Bearer b4m_live_xxxxxxxxxxxx
- Regenerate API key if expired
Error: Invalid or expired token
HTTP Code: 401
Causes:
- JWT token expired
- JWT token invalid or malformed
Solutions:
- Refresh your JWT token
- Verify token hasn't been tampered with
- Check token expiration time
Error: API key does not have permission
Full message: "API key does not have permission for AI completions"
HTTP Code: 403
Causes:
- API key missing required scopes (
ai:generateorai:chat)
Solutions:
- Go to Settings → API Keys
- Edit your API key
- Add
ai:generateorai:chatscope - Save changes and use updated key
Request Validation Errors
Error: Invalid request body
HTTP Code: 400 or 422
Causes:
- Malformed JSON
- Failed schema validation
- Missing required fields
- Invalid field types
Solutions:
- Validate JSON syntax:
echo '{"model": "claude-3-5-sonnet"}' | jq .
- Verify required fields:
model(string)messages(array)
- Check field types match schema
- Review API Reference for correct format
Error: model is required
HTTP Code: 422
Cause: Missing model field in request
Solution:
{
"model": "claude-3-5-sonnet", // Required
"messages": [...]
}
Error: messages is required
HTTP Code: 422
Cause: Missing messages field in request
Solution:
{
"model": "claude-3-5-sonnet",
"messages": [ // Required
{"role": "user", "content": "Hello"}
]
}
Error: Model info not found
Full message: "Model info not found for '{model}'"
HTTP Code: 400
Causes:
- Invalid model identifier
- Typo in model name
- Unsupported model
Solutions:
- Verify model name spelling:
"model": "claude-3-5-sonnet" // Correct"model": "claude-sonnet-3.5" // Wrong
- Use supported models:
claude-3-5-sonnetgpt-4gpt-3.5-turbo- Contact support for full model list
Rate Limiting Errors
Error: Rate limit exceeded
Full message: "Rate limit exceeded" or "Rate limit exceeded: {X} requests per minute allowed"
HTTP Code: 429
Headers:
Retry-After: 30
X-RateLimit-Remaining-Minute: 0
X-RateLimit-Reset-Minute: 1705334430
Causes:
- Too many requests in time window
- Per-minute limit hit
- Per-day limit hit
Solutions:
1. Wait and retry
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || 60;
console.log(`Waiting ${retryAfter}s before retry...`);
await sleep(retryAfter * 1000);
// Retry request
}
2. Check rate limit headers
const minuteRemaining = response.headers.get('X-RateLimit-Remaining-Minute');
const minuteReset = response.headers.get('X-RateLimit-Reset-Minute');
console.log(`${minuteRemaining} requests remaining`);
console.log(`Resets at ${new Date(minuteReset * 1000)}`);
3. Implement client-side rate limiting
const limiter = new RateLimiter(60); // 60 requests/minute
await limiter.waitForSlot();
const response = await fetch(...);
4. Request higher limits
Contact support to increase your rate limits for production use.
Credit/Billing Errors
Error: Insufficient credits
Full message: "Insufficient credits. You have {X} credits, but this request requires approximately {Y} credits."
HTTP Code: 403
Causes:
- Not enough credits in account balance
- Model requires more credits than available
- High
maxTokensvalue increases cost estimate
Solutions:
1. Add credits to account
- Purchase additional credits
- Top up balance in billing settings
2. Use cheaper model
// Expensive
"model": "gpt-4"
// Cheaper alternative
"model": "gpt-3.5-turbo"
3. Reduce maxTokens
{
"options": {
"maxTokens": 500 // Lower limit = lower cost estimate
}
}
4. Monitor credit usage
- Check usage analytics
- Set up billing alerts
- Track costs per request
Model/LLM Errors
Error: Model response error
Full message: "Model response error: {details}"
HTTP Code: 500
Causes:
- LLM provider error
- Model temporarily unavailable
- Unexpected model response
Solutions:
- Retry request after brief delay
- Check provider status page
- Try alternative model if available
- Contact support if persistent
Rate Limit Headers
All responses include rate limit information:
X-RateLimit-Limit-Minute: 60
X-RateLimit-Remaining-Minute: 45
X-RateLimit-Reset-Minute: 1705334400
X-RateLimit-Limit-Day: 1000
X-RateLimit-Remaining-Day: 850
X-RateLimit-Reset-Day: 1705420800
Understanding Rate Limit Headers
| Header | Description | Example |
|---|---|---|
X-RateLimit-Limit-Minute | Max requests per minute | 60 |
X-RateLimit-Remaining-Minute | Remaining this minute | 45 |
X-RateLimit-Reset-Minute | Unix timestamp when resets | 1705334400 |
X-RateLimit-Limit-Day | Max requests per day | 1000 |
X-RateLimit-Remaining-Day | Remaining today | 850 |
X-RateLimit-Reset-Day | Unix timestamp when resets | 1705420800 |
Retry Strategies
Exponential Backoff
Retry transient errors with exponential backoff:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
// Success
if (response.ok) {
return response;
}
// Rate limited - wait and retry
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await sleep(retryAfter * 1000);
continue;
}
// Server error - exponential backoff
if (response.status >= 500) {
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
console.log(`Server error. Retrying in ${delay}ms...`);
await sleep(delay);
continue;
}
// Other errors - don't retry
throw new Error(`HTTP ${response.status}`);
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Retry Decision Matrix
| Error Code | Retryable? | Strategy |
|---|---|---|
| 429 (Rate Limit) | ✅ Yes | Wait Retry-After seconds |
| 500 (Server Error) | ✅ Yes | Exponential backoff (max 3 attempts) |
| 503 (Service Unavailable) | ✅ Yes | Exponential backoff (max 3 attempts) |
| 401 (Unauthorized) | ❌ No | Fix authentication |
| 403 (Forbidden) | ❌ No | Fix permissions/credits |
| 400 (Bad Request) | ❌ No | Fix request format |
| 422 (Validation Error) | ❌ No | Fix request schema |
Troubleshooting Guide
Problem: Authentication Failing
Symptoms:
- HTTP 401 errors
- "Authentication failed" message
Diagnosis:
# Check API key is set
echo $B4M_API_KEY
# Test with curl
curl -X POST https://app.bike4mind.com/api/ai/v1/completions \
-H "X-API-Key: $B4M_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"claude-3-5-sonnet","messages":[{"role":"user","content":"test"}]}'
Solutions:
- Verify API key format (starts with
b4m_) - Check API key hasn't expired
- Regenerate key if needed
- Verify scopes include
ai:generateorai:chat
Problem: Rate Limits Being Hit
Symptoms:
- HTTP 429 errors
- "Rate limit exceeded" messages
- Requests failing during high traffic
Diagnosis:
// Check rate limit headers
console.log('Minute remaining:', headers.get('X-RateLimit-Remaining-Minute'));
console.log('Day remaining:', headers.get('X-RateLimit-Remaining-Day'));
Solutions:
- Implement client-side rate limiting
- Add delays between requests
- Queue requests instead of parallel execution
- Request higher limits from support
- Distribute load across multiple API keys (if allowed)
Problem: Insufficient Credits
Symptoms:
- HTTP 403 errors
- "Insufficient credits" message
- Requests failing unexpectedly
Diagnosis:
// Check error message for credit details
if (error.message.includes('Insufficient credits')) {
const match = error.message.match(/have (\d+) credits.*requires.*(\d+) credits/);
console.log(`Current: ${match[1]}, Required: ${match[2]}`);
}
Solutions:
- Add credits to account
- Switch to cheaper model
- Reduce
maxTokensparameter - Monitor credit usage patterns
- Set up low balance alerts
Problem: Stream Not Working
Symptoms:
- No real-time updates
- Entire response appears at once
- Long wait before output
Diagnosis:
# Test with curl (should stream)
curl --no-buffer -X POST https://app.bike4mind.com/api/ai/v1/completions \
-H "X-API-Key: $B4M_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"claude-3-5-sonnet","messages":[{"role":"user","content":"Count to 10"}]}'
Solutions:
- curl: Use
--no-bufferflag - Python: Use
stream=Truein requests - JavaScript: Use
response.body.getReader() - Check for buffering proxies
- Verify
X-Accel-Buffering: noheader present
Problem: JSON Parsing Errors
Symptoms:
- "Unexpected token" errors
- "Unexpected end of JSON" errors
- Parse failures in event handler
Diagnosis:
try {
const event = JSON.parse(data);
} catch (err) {
console.error('Parse error:', err);
console.error('Raw data:', data); // Log what failed
}
Solutions:
- Buffer incomplete lines properly
- Check for complete
data:format - Wrap JSON.parse in try/catch
- Split by
\n\nfor SSE boundaries - Handle
[DONE]before parsing
Problem: Missing Tool Calls
Symptoms:
- Expected
tool_useevent but gotcontent - Model doesn't call tools
Diagnosis:
// Verify tools are included
console.log('Tools sent:', request.options.tools);
// Check model supports tools
const supportsTools = ['claude', 'gpt-4', 'gpt-3.5-turbo'].some(m =>
model.includes(m)
);
Solutions:
- Verify tools array included in request
- Check tool descriptions are clear
- Ensure
toolSchema.parametersis valid JSON Schema - Try more explicit prompts
- Verify model supports tools
Error Logging Best Practices
What to Log
function logError(error, context) {
console.error({
timestamp: new Date().toISOString(),
error: {
message: error.message,
stack: error.stack,
status: error.status,
},
context: {
model: context.model,
messageCount: context.messages?.length,
hasTools: Boolean(context.options?.tools),
},
rateLimit: {
minuteRemaining: context.headers?.['x-ratelimit-remaining-minute'],
dayRemaining: context.headers?.['x-ratelimit-remaining-day'],
},
// NEVER log: API key, message content, user data
});
}
What NOT to Log
❌ Never log sensitive information:
- API keys
- JWT tokens
- User message content
- Personal user data
- Credentials
Getting Help
If you encounter persistent errors:
1. Check API Status
Verify the API is operational (status page coming soon).
2. Review Logs
Look for patterns:
- Same error repeatedly?
- Specific model causing issues?
- Time-based patterns?
3. Test with curl
Isolate client-side vs server-side issues:
curl -v --no-buffer -X POST https://app.bike4mind.com/api/ai/v1/completions \
-H "X-API-Key: $B4M_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"claude-3-5-sonnet","messages":[{"role":"user","content":"test"}]}'
4. Contact Support
Provide:
- Error messages (exact text)
- HTTP status codes
- Request timestamp
- Model used
- Steps to reproduce
- Do NOT send: API keys, user data
Next Steps
- API Reference - Verify request format
- Authentication - Fix auth issues
- Streaming Guide - Debug streaming problems
- Best Practices - Production error handling