# Errors and troubleshooting

Every API failure has a stable code, a readable message and a link to its exact fix.

```json
{
  "error": {
    "code": "unauthorized",
    "message": "A valid API key is required.",
    "docs_url": "https://metaphrase.msalman.com/docs/errors#unauthorized"
  }
}
```

Use HTTP status and `error.code` in program logic, not English wording. Keep `X-Request-Id` for support. For SSE, HTTP 200 can contain an `error` event after output starts; only `done` marks success. See [streaming](https://metaphrase.msalman.com/docs/streaming.md).

## invalid_file

**HTTP 422.** This document could not be read.

**Fix:** Upload an unencrypted .txt (UTF-8), .docx, or text-based .pdf file, at most 5 MB and within max_input_chars. Scanned PDFs need OCR first.

## invalid_request

**HTTP 400.** Check the request fields and their allowed values.

**Fix:** Send the documented JSON fields with the correct types. Unknown fields are rejected.

## invalid_json

**HTTP 400.** The request body is not valid JSON.

**Fix:** Serialize the request with JSON.stringify or your language's JSON encoder.

## unsupported_media_type

**HTTP 415.** Send application/json content.

**Fix:** Set Content-Type: application/json. File uploads use their separate documented route.

## body_too_large

**HTTP 413.** The request body exceeds the allowed size.

**Fix:** Reduce the payload and split long documents at sentence boundaries.

## text_too_long

**HTTP 413.** The text exceeds the configured character limit.

**Fix:** Use the max_input_chars value in the API specification and split longer documents.

## unauthorized

**HTTP 401.** A valid API key is required.

**Fix:** Send Authorization: Bearer YOUR_KEY. Create a new key if the old one was revoked.

## forbidden

**HTTP 403.** This action is not available to this account.

**Fix:** Check account status and the key's access permissions.

## rate_limited

**HTTP 429.** Too many requests. Please wait and try again.

**Fix:** Wait for Retry-After seconds and use exponential backoff with jitter.

## quota_exceeded

**HTTP 429.** The monthly usage quota has been reached.

**Fix:** Wait for the next UTC calendar month or ask an administrator to change the plan.

## budget_exceeded

**HTTP 429.** The writing service has reached its configured spending limit.

**Fix:** The operator must review usage and adjust the configured budget before more paid requests can run.

## verification_required

**HTTP 403.** Please complete the verification challenge.

**Fix:** Complete Turnstile in the web interface, then submit the new verification token.

## provider_not_configured

**HTTP 503.** The writing provider is not configured yet.

**Fix:** The operator must configure LLM_PRIMARY_API_KEY or a fallback provider in .env and recreate the web container.

## upstream_unavailable

**HTTP 503.** The writing provider is temporarily unavailable.

**Fix:** Retry with backoff. The operator can inspect provider availability without logging submitted text.

## upstream_timeout

**HTTP 504.** The writing provider took too long to respond.

**Fix:** Retry a shorter input or wait before retrying. Do not append a retry to partial streamed output.

## upstream_error

**HTTP 502.** The writing provider returned an invalid response.

**Fix:** Retry once with a shorter input. If it continues, the operator should check the configured model's compatibility.

## output_truncated

**HTTP 502.** The response reached its output limit before finishing.

**Fix:** Use a shorter input or ask the operator to adjust the tool's output cap and chunk size.

## service_unavailable

**HTTP 503.** A required service is temporarily unavailable.

**Fix:** Wait and retry. The operator should check Redis, PostgreSQL, or the tool's health.

## invalid_source

**HTTP 422.** The source could not be used.

**Fix:** Check the DOI, ISBN, or public HTTP URL, or use manual citation fields.

## source_unavailable

**HTTP 422.** Source metadata could not be retrieved.

**Fix:** Try a DOI or ISBN, or enter the source details manually.

## text_too_short

**HTTP 422.** More text is needed for a meaningful estimate.

**Fix:** Submit at least the documented minimum word count. Very short texts cannot be assessed meaningfully.

## not_found

**HTTP 404.** This resource does not exist.

**Fix:** Check the API version, endpoint name, and resource identifier.

## method_not_allowed

**HTTP 405.** This HTTP method is not supported.

**Fix:** Use POST for processing endpoints and the method documented for dashboard routes.

## conflict

**HTTP 409.** This action conflicts with an existing resource.

**Fix:** Use a different identifier or retry after the existing operation has completed.

## internal_error

**HTTP 500.** The request could not be completed.

**Fix:** Retry once. If it persists, provide the X-Request-Id header to the operator.

## Troubleshooting FAQ

**I received HTML instead of JSON.** Check the exact HTTPS origin and path, your reverse proxy/CDN rules and the Content-Type. A CDN challenge or gateway failure is outside the API's JSON handler. SDKs surface it as an upstream error.

**My browser integration works but exposes a key.** Move the upstream request into a backend route; use [the working proxy](https://metaphrase.msalman.com/docs/recipe-html.md). CORS does not make a secret safe in frontend code.

**My provider is not configured.** This is an operator setting. Grammar, citations and extractive summaries still work. Generative tools require a provider configured on MetaPhrase, not an OpenAI key from every API consumer.

**My grammar offsets are wrong with emoji.** Offsets/lengths use UTF-16 code units. JavaScript slice works directly; Python clients need UTF-16 conversion, as shown on the grammar page.

**My request succeeded but quota decreased for a cache hit.** Accepted calls count against monthly quota; cache hits avoid model tokens, not request accounting. All tools share each key's cap.

**The SDK raised an error with status=null.** This is a client network/timeout failure before an HTTP status was received. Check connectivity and the configured base origin. Retrying paid work may incur another attempt, so use bounded retries.

**The widget is blank or denied.** Check the public widget ID, active linked key, exact scheme/host/port origin and your site's script/frame CSP. See [widget setup](https://metaphrase.msalman.com/docs/widget.md).
