Skip to Content
GatewayTroubleshooting

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:

  1. Key is present — Ensure you are sending the gateway key via one of the accepted methods:

    • X-CM-API-Key header (recommended)
    • Authorization: Bearer <gateway-key> header
    • api_key query parameter (for SSE/EventSource only)
  2. Key has the correct prefix — Valid gateway keys start with one of: cm_sk_, cm_gw_, cm-gw-, cmgw_, cm_, or sk_live_. If your key does not start with one of these prefixes, the gateway will not recognize it.

  3. Key is active — Keys that have been revoked or disabled will fail validation. Check your key status in the dashboard .

  4. 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.

  1. 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 the X-Provider-Key header.

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:

  1. Check your plan tier — Each plan has different model access:

    PlanModel Access
    Freegpt-4o-mini, claude-3-haiku, gemini-2.0-flash only
    StarterAll models except o1, o1-mini, o1-preview, o3, o3-mini
    Pro / TeamAll models
    EnterpriseAll models
  2. 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.

  3. 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: 1708642860

Default RPM limits by tier:

PlanRPM Limit
Free10
Starter60
Pro300
Team1,000
Enterprise5,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:

PlanDaily Requests
Free1,000
Starter100,000
Pro / Growth500,000
TeamUnlimited
EnterpriseUnlimited

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:

PlanPer-Request MaxDaily BudgetMonthly 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 (or max_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:

CategoryExamples
API keys & secretsOpenAI keys (sk-...), Anthropic keys (sk-ant-...), AWS keys (AKIA...), Stripe keys, GitHub tokens, bearer tokens, password=... patterns
Personal identifiersSocial Security Numbers (xxx-xx-xxxx), credit card numbers (Luhn-validated), email addresses, phone numbers
EU complianceIBAN numbers, EU VAT numbers, EU passport numbers, UK National Insurance numbers, German ID numbers
Health dataICD-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:

  1. 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]"
  2. 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.

  3. 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_action field set to ALLOW (log-only) or BLOCK (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_tokens to request shorter responses.
  • The gateway retries automatically with exponential backoff for transient failures. If retries are exhausted, the X-Gateway-Retry-Attempts response 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:

PlanHITL 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/openai path suffix — the base URL must include the provider path segment.
  • Putting the gateway key in api_key instead of default_headers — the api_key field is for the provider key.
  • Using https://api.curate-me.ai/v1 without 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:

  1. 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; and X-Accel-Buffering: no to 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).
  2. EventSource cannot send custom headers — If you are using the browser’s native EventSource API for SSE, you cannot send custom headers. Use the api_key query parameter instead:

    https://api.curate-me.ai/v1/openai/chat/completions?api_key=cm_sk_xxx

    For production use, prefer a library like eventsource-parser that allows custom headers.

  3. SSE format — The gateway forwards SSE data lines exactly as received from the provider. Each chunk is formatted as data: {json}\n\n and the stream ends with data: [DONE]\n\n. If you see mid-stream errors, they are emitted as structured SSE error events so your client can detect and retry.

  4. Timeout on long-running streams — The gateway’s default read timeout is 120 seconds. For very long completions (e.g., large max_tokens on 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:

MetricLocationDelay
Rate limit countersRedisImmediate
Daily cost accumulatorRedisImmediate
Detailed usage recordsMongoDBUp to 30 seconds
Dashboard cost chartsDashboard UIUp to 60 seconds (polling interval)

Checklist:

  1. Wait at least 30 seconds — After making a request, allow time for the async cost recording pipeline to flush data from Redis to MongoDB.

  2. 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.

  3. 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.

  4. 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:

  1. 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 set Access-Control-Allow-Credentials: true, so credentialed requests will fail. Solution: do not send credentials; use API key headers instead.

  2. Your custom headers are not in the allowed list — The gateway allows these headers:

    • Content-Type, Authorization, X-CM-API-Key, X-Provider-Key
    • X-Org-ID, X-Request-ID, X-Idempotency-Key, Idempotency-Key
    • X-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.

  3. 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:

HeaderDescription
X-Gateway-Retry-AttemptsNumber of attempts made (1 = no retry)
X-Gateway-Retry-Delay-MsTotal 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:

HeaderDescription
X-CM-Key-Deprecatedtrue if the key has been rotated
X-CM-Grace-Remaining-HoursHours 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:

StatusCodeBlocked ByMeaning
401invalid_api_keyauthMissing or invalid gateway API key
401key_rotatedauthAPI key was rotated; use the replacement
403cost_per_requestgovernanceEstimated cost exceeds per-request limit
403daily_budgetgovernanceDaily budget exhausted
403monthly_budgetgovernanceMonthly budget exhausted
403model_allowlistgovernanceModel not in org’s allowed list
403plan_entitlementgovernanceLegacy daily request quota or budget exceeded
413request_too_largesize limitRequest body exceeds 10 MB
429rate_limitgovernancePer-minute request limit exceeded
429plan_enforcementplan enforcementDaily plan request limit exceeded
202needs_approvalgovernanceHuman approval required before execution
403pii_scangovernancePII or secrets detected in request
500internal_errorproxyInternal gateway error
502connection_errorproxyCannot connect to upstream provider
503service_unavailableshutdownGateway is shutting down (retry shortly)
503circuit_breaker_openproxyUpstream provider circuit breaker tripped
504timeoutproxyUpstream provider timed out

Getting Help

If you cannot resolve an issue using this guide:

  1. Collect the X-CM-Request-ID from the response headers.
  2. Note the exact error message and status code.
  3. Check the dashboard  for usage data and governance policy settings.
  4. Contact support at support@curate-me.ai with the request ID and error details.