Errors, Rate Limits & Troubleshooting
Every error family on the platform, what causes it, and how to get unstuck.
Response envelopes by API familylink
| Family | Error shape |
|---|---|
| Business REST (support tickets, secret manager, auth verify) | { "statusCode": 401, "error": "Unauthorized", "message": "…", "correlationId": "req-…", "timestamp": "…" } (+ details on validation errors) |
| BugWatch ingest / symbols / deploy | { "error": "…" } with the HTTP status carrying the semantics |
OTLP (/v1/logs · /v1/traces · /v1/metrics) | { "partialSuccess": { "rejectedLogRecords": 0, "errorMessage": "…" } } with the mapped status |
| Prometheus query | { "status": "error", "errorType": "unauthorized" | "execution", "error": "…" } |
| Jaeger | { "data": null, "errors": [{ "msg": "…" }] } |
| MCP | JSON-RPC error object, e.g. { "jsonrpc": "2.0", "error": { "code": -32001, "message": "…" }, "id": null } |
Every REST response echoes X-Correlation-ID (send your own x-correlation-id header to propagate yours). Quote it when contacting support — it is the fastest path to your exact request in the logs.
Error cataloguelink
| Status | Message you'll see | Cause | Fix |
|---|---|---|---|
| 401 | Missing API key. | No x-api-key header | Send x-api-key: KEYID:secret |
| 401 | Invalid API key format. Expected: <keyId>:<secretKey> | Key ID sent without the secret to a server endpoint | Use the full keyId:secret string (shown once at creation) |
| 401 | Invalid or inactive API key | Unknown key ID, revoked, or expired | Check Dashboard → API Keys; create/rotate if needed |
| 401 | Invalid API key | Key ID exists but the secret is wrong | Re-copy the secret; rotating invalidates old secrets immediately |
| 403 | Business account is not active | Org suspended | Resolve account status in the dashboard |
| 403 | API key does not have access to BugWatch | Key has no bugwatch product scope | Recreate/edit the key with the right product |
| 403 | API key lacks the … permission | Key is missing a granular scope (ingest:write, deploy:write, symbols:*) | Add the scope, or use the project's DSN key |
| 403 | origin_not_allowed | Browser ingest Origin not in the project's allowed origins | Add your site origin at Dashboard → project → Settings (empty list = unrestricted) |
| 400 | use the DSN key from Settings > DSN keys | Key has zero or multiple BugWatch resource scopes | Ingest requires a per-project DSN key, not an org-wide key |
| 404 | BugWatch project not found | Project deleted or key's resource scope stale | Recreate a DSN key from a live project |
| 404 | deployment not found / project not found | Wrong deployId or another project's key | Ids are per-project; use the id returned by Start deployment |
| 409 | (symbols complete) | The file was never PUT to uploadUrl | PUT the bytes, then call complete |
| 429 | rate_limited / Too Many Requests | See limits below | Honour Retry-After; batch events instead of sending singly |
| 413 | Payload too large | Body over the per-route cap | Ingest ≤ 2 MB, OTLP ≤ 4 MB, RUM ≤ 64 KB — split batches |
Rate limitslink
| Surface | Limit | Keyed by | On 429 |
|---|---|---|---|
| REST API (business endpoints) | 100 requests / 60 s | organisation | Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining: 0, X-RateLimit-Reset headers |
| OTLP / Prometheus / Jaeger / RUM | 600,000 / min | BugWatch project | protocol-shaped 429 body, no headers |
| Mobile ingest | 12,000 / 60 s | BugWatch project | { "error": "rate_limited" } |
Successful business-REST responses carry X-RateLimit-Limit only — don't parse for a remaining count on 2xx.
Troubleshooting quick pathslink
- Works in curl, fails in the browser → browsers must use the session flow (
x-bugwatch-session), neverx-api-key. The API's CORS policy does not acceptx-api-keyfrom browsers by design. - Browser ingest suddenly 403s → session tokens expire after ~1 h. Re-fetch from your mint endpoint on 403 and retry once.
- Mobile events silently missing → device clock skew: tokens are rejected if
iatis > 60 s in the future or past expiry (5 min). The SDKs re-sign per batch, so persistent loss usually means a rotatedmobileAppSecret— re-issue credentials to the app. - Events accepted (202) but not visible → check the key's BugWatch environment (test-mode keys write to the project's non-production env), then
eventIddedup (duplicates within 10 min incrementdeduped, notingested). - Stack traces unreadable → symbols: mappings/source maps match on release + platform (must equal the SDK's
releaseexactly); Apple/native match by debug UUID. Check Get upload status forCHECKSUM_MISMATCH,UNRECOGNIZED_ARCHIVE,ARCHIVE_PLATFORM_MISMATCH, then Reprocess to fix already-captured crashes. - Webhook receiver never fires → destination must be public HTTPS (private/internal IPs are rejected), and delivery is single-shot with a 5 s timeout — a cold-starting receiver misses events.
- Chat widget button never appears → the widget hides until key auth passes; check the
onAuthcallback. Rejection reasons surface asMISSING | MALFORMED | UNKNOWN_ENVIRONMENT | KEY_NOT_FOUND | INACTIVE | EXPIRED | SECRET_MISMATCH | ORG_NOT_FOUND. - AI chat replies with a manipulation warning → the platform scans prompts for injection; malicious content is rejected (and for signed-in dashboard users, repeated attempts block the account — a super-admin must lift it).