Troubleshooting
This guide covers the most common errors and integration issues you may encounter when using the Curate-Me AI Gateway. Every error response follows the OpenAI-compatible format:
{
"error": {
"message": "Human-readable description",
"type": "error_category",
"param": null,
"code": "machine_readable_code"
}
}Common Errors
401 Unauthorized — Invalid or Missing API Key
Error codes: invalid_api_key, key_rotated
The gateway could not authenticate your request. This means the Curate-Me gateway key (not the upstream provider key) is missing, malformed, expired, or has been rotated.
Typical response:
{
"error": {
"message": "Missing or invalid credentials. Provide a valid gateway key via X-CM-API-Key / Authorization: Bearer <gateway-key>, or a valid dashboard access token via Authorization: Bearer <jwt>.",
"type": "authentication_error",
"param": null,
"code": "invalid_api_key"
}
}Checklist:
-
Key is present — Ensure you are sending the gateway key via one of the accepted methods:
X-CM-API-Keyheader (recommended)Authorization: Bearer <gateway-key>headerapi_keyquery parameter (for SSE/EventSource only)
-
Key has the correct prefix — Valid gateway keys start with one of:
cm_sk_,cm_gw_,cm-gw-,cmgw_,cm_, orsk_live_. If your key does not start with one of these prefixes, the gateway will not recognize it. -
Key is active — Keys that have been revoked or disabled will fail validation. Check your key status in the dashboard .
-
Key has not been rotated — If you recently rotated your key, the old key enters a grace period and then expires. A rotated key returns a specific error:
{
"error": {
"message": "API key has been rotated. Use the new key. Replacement key ID: key_abc123",
"type": "authentication_error",
"code": "key_rotated"
}
}Update your application to use the replacement key indicated in the error message.
- Provider key is separate — The gateway requires two credentials: your Curate-Me gateway key and your upstream provider key (e.g., your OpenAI API key). Do not confuse the two. Pass the provider key via
Authorization: Bearer <provider-key>or theX-Provider-Keyheader.
403 Forbidden — Model Not Allowed
Blocked by: model_allowlist or plan_enforcement
The requested model is not permitted for your organization. This can happen due to plan-tier restrictions or a custom governance policy.
Typical responses:
Plan-tier restriction:
{
"error": {
"message": "Model 'o3' is not available on the starter plan. Upgrade to Pro or Enterprise for access. Upgrade at https://dashboard.curate-me.ai/settings/billing",
"type": "insufficient_quota",
"code": "plan_limit_exceeded"
}
}Custom allowlist restriction:
{
"error": {
"message": "Model 'gpt-5.1-chat-latest' is not in the allowed list: ['gpt-4o', 'gpt-4o-mini']",
"type": "permission_error",
"code": "403"
}
}Checklist:
-
Check your plan tier — Each plan has different model access:
Plan Model Access Free gpt-4o-mini,claude-3-haiku,gemini-2.0-flashonlyStarter All models except o1,o1-mini,o1-preview,o3,o3-miniPro / Team All models Enterprise All models -
Check your governance policy — Your organization may have a custom model allowlist configured in the dashboard under Settings > Governance Policies. An admin can update the allowed models list.
-
Upgrade your plan — If you need access to restricted models, upgrade at dashboard.curate-me.ai/settings/billing .
429 Too Many Requests — Rate Limited or Plan Quota Exceeded
Blocked by: rate_limit or plan_entitlement or plan_enforcement
A 429 error has two distinct flavors. Check the error message and response headers to distinguish them.
Flavor 1: Per-Minute Rate Limit
Your organization has exceeded the requests-per-minute (RPM) limit.
{
"error": {
"message": "Rate limit exceeded: 61/60 requests per minute",
"type": "rate_limit_error",
"code": "429"
}
}Response headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1708642860Default RPM limits by tier:
| Plan | RPM Limit |
|---|---|
| Free | 10 |
| Starter | 60 |
| Pro | 300 |
| Team | 1,000 |
| Enterprise | 5,000 |
Resolution: Wait until the X-RateLimit-Reset timestamp (typically less than 60 seconds), then retry. Consider implementing exponential backoff in your client. If you consistently hit the limit, upgrade your plan or request a custom RPM limit through the dashboard.
Flavor 2: Daily Plan Quota
Your organization has exhausted its daily request count or daily budget.
{
"error": {
"message": "Daily request limit exceeded. Your starter plan allows 50,000 requests/day. Upgrade at https://dashboard.curate-me.ai/settings/billing",
"type": "insufficient_quota",
"code": "plan_limit_exceeded"
}
}Or for daily budget:
{
"error": {
"message": "Daily budget limit reached. Upgrade your plan at dashboard.curate-me.ai/settings/billing",
"type": "rate_limit_error",
"code": "429"
}
}Daily request limits by tier:
| Plan | Daily Requests |
|---|---|
| Free | 1,000 |
| Starter | 100,000 |
| Pro / Growth | 500,000 |
| Team | Unlimited |
| Enterprise | Unlimited |
Resolution: Daily counters reset at midnight UTC. To check your current usage, visit the cost tracking page in the dashboard. If you need higher limits, upgrade your plan.
403 Forbidden — Budget Controls
Blocked by: cost_per_request, daily_budget, or monthly_budget
The gateway estimated that processing this request would exceed your cost limits.
Typical responses:
Per-request cost exceeded:
{
"error": {
"message": "Estimated cost $2.5000 exceeds per-request limit $0.50",
"type": "permission_error",
"code": "cost_per_request"
}
}Daily budget exceeded:
{
"error": {
"message": "Daily budget exhausted: $24.50 spent + $0.8500 estimated > $25.00 limit",
"type": "permission_error",
"code": "daily_budget"
}
}Monthly budget exceeded:
{
"error": {
"message": "Monthly budget exhausted: $248.00 spent + $3.0000 estimated > $250.00 limit",
"type": "permission_error",
"code": "monthly_budget"
}
}Default budget limits by tier:
| Plan | Per-Request Max | Daily Budget | Monthly Budget |
|---|---|---|---|
| Free | $0.25 | $5 | $50 |
| Starter | $0.50 | $25 | $250 |
| Pro / Growth | $2.00 | $100 | $2,000 |
| Team | $5.00 | $500 | $10,000 |
| Enterprise | $10.00 | $2,000 | $50,000 |
Resolution:
- For per-request denials, reduce the
max_tokens(ormax_completion_tokens) in your request body. The gateway estimates cost based on this value. - For daily/monthly budget denials, wait for the budget window to reset (midnight UTC for daily, first of month for monthly), or upgrade your plan.
- Custom budget limits can be set per-organization via the governance policy in the dashboard.
413 Payload Too Large — Request Body Exceeds 10 MB
Error code: request_too_large
The gateway enforces a maximum request body size of 10 MB to prevent abuse and ensure stability.
{
"error": {
"message": "Request body too large. Maximum size is 10485760 bytes (10 MB).",
"type": "invalid_request_error",
"param": null,
"code": "request_too_large"
}
}Resolution:
- Reduce the size of your request. Large payloads are typically caused by embedding images as base64 in the message content. Consider using image URLs instead of inline base64 when possible.
- If you are sending tool/function definitions, check whether you are including unnecessarily large JSON schemas.
403 Forbidden — PII Detected
Blocked by: pii_scan
The gateway scanned your request content and found personally identifiable information (PII) or secrets that should not be sent to an LLM provider.
{
"error": {
"message": "PII detected in request: credit_card, email, ssn",
"type": "permission_error",
"code": "pii_scan"
}
}What triggers the PII scanner:
| Category | Examples |
|---|---|
| API keys & secrets | OpenAI keys (sk-...), Anthropic keys (sk-ant-...), AWS keys (AKIA...), Stripe keys, GitHub tokens, bearer tokens, password=... patterns |
| Personal identifiers | Social Security Numbers (xxx-xx-xxxx), credit card numbers (Luhn-validated), email addresses, phone numbers |
| EU compliance | IBAN numbers, EU VAT numbers, EU passport numbers, UK National Insurance numbers, German ID numbers |
| Health data | ICD-10 codes, medication dosage patterns |
When the DLP_GUARDRAILS feature flag is enabled, the gateway also uses Presidio NER (Named Entity Recognition) for more advanced detection beyond regex patterns.
Resolution:
-
Sanitize your input — Remove or redact PII from your prompts before sending them to the gateway. Replace real data with placeholders:
# Before "The customer email is john@example.com and SSN is 123-45-6789" # After "The customer email is [EMAIL] and SSN is [REDACTED]" -
Disable PII scanning per-org — If your use case legitimately requires sending PII (e.g., a HIPAA-compliant workflow with appropriate safeguards), an organization admin can disable PII scanning in the governance policy via the dashboard. The policy field is
pii_scan_enabled. -
Adjust the PII action — By default, PII detection blocks the request. The governance policy can be configured to log a warning instead of blocking, using the
pii_actionfield set toALLOW(log-only) orBLOCK(default).
503 Service Unavailable — Gateway Shutting Down or Provider Down
Error codes: service_unavailable, circuit_breaker_open
A 503 error occurs in two situations:
Gateway is shutting down (during deploys)
When the gateway is restarting (e.g., during a deployment), in-flight requests are drained and new requests receive a 503.
Resolution: Retry after a few seconds. The Retry-After header, when present, indicates how long to wait. This is transient and resolves once the new instance is ready. Production deployments use rolling restarts, so downtime is typically under 30 seconds.
Upstream provider circuit breaker is open
If an upstream provider (OpenAI, Anthropic, etc.) experiences sustained failures, the gateway’s circuit breaker trips to prevent cascading timeouts.
{
"error": {
"message": "Provider openai is temporarily unavailable (circuit breaker open)",
"type": "service_unavailable",
"param": null,
"code": "circuit_breaker_open"
}
}Resolution: The circuit breaker will automatically close after a recovery period once the provider starts responding successfully again. Check the provider’s status page and retry after a short delay. You can monitor circuit breaker status at GET /v1/providers/health.
504 Gateway Timeout — Upstream Provider Timed Out
Error code: timeout
The upstream LLM provider did not respond within the timeout window (default: 120 seconds).
{
"error": {
"message": "Upstream provider timed out after 120.0s",
"type": "timeout_error",
"param": null,
"code": "timeout"
}
}Resolution:
- For non-streaming requests, consider switching to streaming mode (
"stream": true), which is more resilient to slow responses since the connection stays alive as tokens arrive. - Reduce
max_tokensto request shorter responses. - The gateway retries automatically with exponential backoff for transient failures. If retries are exhausted, the
X-Gateway-Retry-Attemptsresponse header shows how many attempts were made.
502 Bad Gateway — Cannot Connect to Provider
Error code: connection_error
The gateway could not establish a connection to the upstream LLM provider.
{
"error": {
"message": "Failed to connect to openai API",
"type": "connection_error",
"param": null,
"code": "connection_error"
}
}Resolution: This typically indicates a DNS or network issue between the gateway and the provider. Check the provider’s status page. The gateway retries connection failures automatically. If the issue persists, check GET /v1/providers/health for circuit breaker status.
202 Accepted — Request Held for Human Approval (HITL)
Blocked by: hitl_gate
This is not an error per se. When a request’s estimated cost exceeds the HITL (Human-in-the-Loop) threshold, the gateway holds the request for manual approval instead of proxying it immediately.
{
"error": {
"message": "Estimated cost $12.5000 exceeds HITL threshold $10.00",
"type": "rate_limit_error",
"code": "202"
}
}The response includes an X-CM-Approval-ID header. Use this ID to poll for approval status:
curl https://api.curate-me.ai/v1/approvals/{approval_id}/status \
-H "X-CM-API-Key: cm_sk_xxx"HITL thresholds by tier:
| Plan | HITL Threshold |
|---|---|
| Free | $1.00 |
| Starter | $3.00 |
| Pro | $10.00 |
| Team | $25.00 |
| Enterprise | $50.00 |
Resolution: An administrator can approve or reject the request in the dashboard under Approvals. HITL thresholds can be adjusted in the governance policy if the default is too conservative for your workload.
Subscription Inactive
Blocked by: plan_enforcement
Your organization’s subscription has been canceled or has expired.
{
"error": {
"message": "Subscription is canceled. Reactivate at https://dashboard.curate-me.ai/settings/billing",
"type": "insufficient_quota",
"code": "plan_limit_exceeded"
}
}Active statuses that allow requests: active, trialing, past_due. Requests are blocked for canceled, expired, or missing subscriptions.
Resolution: Reactivate your subscription at dashboard.curate-me.ai/settings/billing .
Integration Issues
”My OpenAI SDK is not working with the gateway”
The most common integration mistake is incorrect base_url or missing the gateway key header.
Python (OpenAI SDK):
from openai import OpenAI
client = OpenAI(
# Point to the gateway, not directly to OpenAI
base_url="https://api.curate-me.ai/v1/openai",
# Your OpenAI API key (provider key)
api_key="sk-your-openai-key",
# Your Curate-Me gateway key
default_headers={"X-CM-API-Key": "cm_sk_your_gateway_key"},
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
)TypeScript (OpenAI SDK):
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.curate-me.ai/v1/openai",
apiKey: "sk-your-openai-key",
defaultHeaders: {
"X-CM-API-Key": "cm_sk_your_gateway_key",
},
});
const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
});Common mistakes:
- Forgetting the
/v1/openaipath suffix — the base URL must include the provider path segment. - Putting the gateway key in
api_keyinstead ofdefault_headers— theapi_keyfield is for the provider key. - Using
https://api.curate-me.ai/v1without the provider segment — the gateway needs to know which provider to route to.
For Anthropic:
import anthropic
client = anthropic.Anthropic(
base_url="https://api.curate-me.ai/v1/anthropic",
api_key="sk-ant-your-anthropic-key",
default_headers={"X-CM-API-Key": "cm_sk_your_gateway_key"},
)For DeepSeek (uses OpenAI-compatible format):
from openai import OpenAI
client = OpenAI(
base_url="https://api.curate-me.ai/v1/deepseek",
api_key="sk-your-deepseek-key",
default_headers={"X-CM-API-Key": "cm_sk_your_gateway_key"},
)“Streaming responses are breaking”
The gateway supports full SSE (Server-Sent Events) passthrough for streaming responses. If streaming is not working correctly, check the following:
-
Reverse proxies and CDNs that buffer responses — Services like Cloudflare, Nginx, or AWS ALB may buffer SSE responses, causing them to arrive in large chunks instead of streaming. Configure your proxy layer to disable buffering for the gateway endpoint:
- Nginx: Add
proxy_buffering off;andX-Accel-Buffering: noto the gateway location block. - Cloudflare: SSE is supported by default but may be delayed by the “Auto Minify” feature. Disable it for the gateway subdomain.
- AWS ALB: Ensure idle timeout is set high enough (at least 120 seconds).
- Nginx: Add
-
EventSource cannot send custom headers — If you are using the browser’s native
EventSourceAPI for SSE, you cannot send custom headers. Use theapi_keyquery parameter instead:https://api.curate-me.ai/v1/openai/chat/completions?api_key=cm_sk_xxxFor production use, prefer a library like
eventsource-parserthat allows custom headers. -
SSE format — The gateway forwards SSE data lines exactly as received from the provider. Each chunk is formatted as
data: {json}\n\nand the stream ends withdata: [DONE]\n\n. If you see mid-stream errors, they are emitted as structured SSE error events so your client can detect and retry. -
Timeout on long-running streams — The gateway’s default read timeout is 120 seconds. For very long completions (e.g., large
max_tokenson slow models), the connection may time out if no data flows. Streaming keeps the connection alive as tokens arrive, so this is rarely an issue. If you encounter it, check that your proxy layer’s timeout is >= 120 seconds.
”My costs are not showing up”
Cost recording is asynchronous. After a request completes, the gateway records token usage and cost data to Redis (for real-time aggregation) and MongoDB (for the immutable audit trail).
Expected delays:
| Metric | Location | Delay |
|---|---|---|
| Rate limit counters | Redis | Immediate |
| Daily cost accumulator | Redis | Immediate |
| Detailed usage records | MongoDB | Up to 30 seconds |
| Dashboard cost charts | Dashboard UI | Up to 60 seconds (polling interval) |
Checklist:
-
Wait at least 30 seconds — After making a request, allow time for the async cost recording pipeline to flush data from Redis to MongoDB.
-
Check the request succeeded — Failed requests (4xx/5xx from the gateway) that are blocked before reaching the provider do not incur cost and will not appear in cost reports. Requests that fail at the provider (upstream 4xx/5xx) are recorded with zero token usage.
-
Verify the X-CM-Request-ID header — Use this header value from your response to look up the specific request in the dashboard under Usage > Request Log.
-
Streaming requests — For streaming responses, final token counts are extracted from the last SSE chunk. If the stream was interrupted (client disconnect, network error), the usage record may show estimated rather than actual token counts.
”I am getting CORS errors”
The gateway’s CORS configuration allows requests from all origins (*) and does not use credentials (allow_credentials=false). This means standard browser-based API calls should work without CORS issues.
However, you may see CORS errors if:
-
Your request includes credentials — If your client sends cookies or uses
credentials: "include"in fetch, browsers require the server to respond with a specific origin (not*). The gateway does not setAccess-Control-Allow-Credentials: true, so credentialed requests will fail. Solution: do not send credentials; use API key headers instead. -
Your custom headers are not in the allowed list — The gateway allows these headers:
Content-Type,Authorization,X-CM-API-Key,X-Provider-KeyX-Org-ID,X-Request-ID,X-Idempotency-Key,Idempotency-KeyX-Runner-ID,X-Session-ID,API-Version
If you are sending a custom header not in this list, the preflight OPTIONS request will fail. Contact support to add your header.
-
Your proxy layer strips CORS headers — If you have a reverse proxy between the browser and the gateway, it may strip or override the
Access-Control-*headers. Ensure your proxy passes these headers through.
Debugging Tools
X-CM-Request-ID Response Header
Every gateway response includes an X-CM-Request-ID header with a unique identifier for that request. Save this value when reporting issues:
curl -v https://api.curate-me.ai/v1/openai/chat/completions \
-H "X-CM-API-Key: cm_sk_xxx" \
-H "Authorization: Bearer sk-your-openai-key" \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}' \
2>&1 | grep -i "x-cm-request-id"Include this ID in support tickets so the team can trace the full request lifecycle in the audit trail.
Retry and Resilience Headers
When the gateway retries a request due to transient upstream failures, the response includes:
| Header | Description |
|---|---|
X-Gateway-Retry-Attempts | Number of attempts made (1 = no retry) |
X-Gateway-Retry-Delay-Ms | Total time spent in retry backoff (ms) |
These headers help diagnose whether a slow response was due to retries or genuinely slow upstream processing.
Dashboard Cost Tracking
The dashboard at dashboard.curate-me.ai/costs provides:
- Real-time daily and monthly spend totals
- Per-model cost breakdown
- Per-request usage log (searchable by request ID)
- Budget utilization graphs
Gateway Health Endpoint
Check the overall health of the gateway and its upstream providers:
# Simple liveness check (no auth required)
curl https://api.curate-me.ai/health
# Detailed health with provider status (auth required)
curl https://api.curate-me.ai/v1/health \
-H "X-CM-API-Key: cm_sk_xxx"
# Provider circuit breaker status
curl https://api.curate-me.ai/v1/providers/health \
-H "X-CM-API-Key: cm_sk_xxx"The /v1/providers/health endpoint returns the circuit breaker state for each provider (closed = healthy, open = failing, half_open = recovering).
Key Rotation Warnings
When your API key is approaching the end of its rotation grace period, the gateway adds deprecation headers to every response:
| Header | Description |
|---|---|
X-CM-Key-Deprecated | true if the key has been rotated |
X-CM-Grace-Remaining-Hours | Hours until the old key stops working |
Monitor these headers in your application to rotate keys before they expire.
Error Response Reference
Quick reference for all gateway error status codes:
| Status | Code | Blocked By | Meaning |
|---|---|---|---|
| 401 | invalid_api_key | auth | Missing or invalid gateway API key |
| 401 | key_rotated | auth | API key was rotated; use the replacement |
| 403 | cost_per_request | governance | Estimated cost exceeds per-request limit |
| 403 | daily_budget | governance | Daily budget exhausted |
| 403 | monthly_budget | governance | Monthly budget exhausted |
| 403 | model_allowlist | governance | Model not in org’s allowed list |
| 403 | plan_entitlement | governance | Legacy daily request quota or budget exceeded |
| 413 | request_too_large | size limit | Request body exceeds 10 MB |
| 429 | rate_limit | governance | Per-minute request limit exceeded |
| 429 | plan_enforcement | plan enforcement | Daily plan request limit exceeded |
| 202 | needs_approval | governance | Human approval required before execution |
| 403 | pii_scan | governance | PII or secrets detected in request |
| 500 | internal_error | proxy | Internal gateway error |
| 502 | connection_error | proxy | Cannot connect to upstream provider |
| 503 | service_unavailable | shutdown | Gateway is shutting down (retry shortly) |
| 503 | circuit_breaker_open | proxy | Upstream provider circuit breaker tripped |
| 504 | timeout | proxy | Upstream provider timed out |
Getting Help
If you cannot resolve an issue using this guide:
- Collect the
X-CM-Request-IDfrom the response headers. - Note the exact error message and status code.
- Check the dashboard for usage data and governance policy settings.
- Contact support at support@curate-me.ai with the request ID and error details.