# Error reference Every error the API returns — status, legacy body and /api/v1 envelope — generated by calling the server, plus the requests that are clamped or empty instead. - Page: - Updated: 2026-10-24 - From the Sri Guru Granth Sahib Ji — Knowledge Base wiki. Sri Guru Granth Sahib Ji is quoted verbatim, cited by Ang, and never altered. --- Every error below was produced by calling the server's own handler, on both surfaces, when this page was generated; CI regenerates it and fails if a status, a message or an envelope has changed. A client should branch on the **status** (and, on `/api/v1`, the `code`), never on the message text. ## The two shapes | | `/api/…` (legacy) | `/api/v1/…` | |---|---|---| | Bad parameter | `400`, `{"error": "invalid request: "}` | `400`, `{"error": {"code": "invalid_request", "message", "request_id"}}` | | Not found (a gap, an unknown bani) | `404`, `{"error": ""}` | `404`, code `not_found` | | Unknown endpoint | `400`, `{"error": "invalid request: unknown endpoint"}` | `404`, code `not_found` | | Unexpected failure | `500`, `{"error": "internal server error"}` | `500`, code `internal`, message `internal server error` | The legacy shape is frozen: the golden contract and the app depend on it byte for byte. New clients should use `/api/v1`, whose `request_id` is the same id as the `X-Request-Id` header and the access log ([versioning and caching](https://docs.gurbanisoul.com/api/versioning-and-caching/)). A `500` never carries the exception; the detail is only in the server log, under that request id. ## Every error, as the server answers it | Request | When | Status | Legacy body | `/api/v1` code · message | |---|---|--:|---|---| | `/api/nope` | a path that is not a route | 400 · v1 404 | `{"error": "invalid request: unknown endpoint"}` | `not_found` · no such endpoint: /api/v1/nope | | `/api/ang` | a route that needs an id, without one | 400 | `{"error": "invalid request: /api/ang requires an id"}` | `invalid_request` · /api/ang requires an id | | `/api/ang/abc` | an id that is not an integer | 400 | `{"error": "invalid request: integer parameter expected"}` | `invalid_request` · integer parameter expected | | `/api/ang/1234567890123` | an integer longer than 12 characters | 400 | `{"error": "invalid request: integer parameter too long"}` | `invalid_request` · integer parameter too long | | `/api/search?q=naam&limit=abc` | an integer parameter that is not an integer | 400 | `{"error": "invalid request: integer parameter expected"}` | `invalid_request` · integer parameter expected | | `/api/shabad/1` | a `comp_id` that is a permanent gap | 404 | `{"error": "no composition with comp_id 1"}` | `not_found` · no composition with comp_id 1 | | `/api/shabad/99999999` | a `comp_id` beyond the last composition | 404 | `{"error": "no composition with comp_id 99999999"}` | `not_found` · no composition with comp_id 99999999 | | `/api/search?q=‹301 characters›` | a search query over 300 characters | 400 | `{"error": "invalid request: query too long (max 300 chars)"}` | `invalid_request` · query too long (max 300 chars) | | `/api/verify?q=` | an empty claim | 400 | `{"error": "invalid request: empty claim"}` | `invalid_request` · empty claim | | `/api/verify?q=‹601 characters›` | a claim over 600 characters | 400 | `{"error": "invalid request: claim too long (max 600 chars)"}` | `invalid_request` · claim too long (max 600 chars) | | `/api/bani` | a bani without its key | 400 | `{"error": "invalid request: /api/bani requires a key"}` | `invalid_request` · /api/bani requires a key | | `/api/bani/NOPE!` | a key with characters a key never has | 400 | `{"error": "invalid request: bad bani key"}` | `invalid_request` · bad bani key | | `/api/bani/nope` | a well-formed key that is not a bani | 404 | `{"error": "no bani with key nope"}` | `not_found` · no bani with key nope | | `/api/bani/japji?variant=zz` | a variant that is not one of the bani's | 400 | `{"error": "invalid request: bad variant"}` | `invalid_request` · bad variant | | `/api/analytics/progression` | progression without `raag` | 400 | `{"error": "invalid request: progression requires a raag"}` | `invalid_request` · progression requires a raag | | `/api/timing/raag` | a raag lookup without `name` | 400 | `{"error": "invalid request: timing/raag requires ?name="}` | `invalid_request` · timing/raag requires ?name= | | `/api/forms?comp_id=0` | forms without a usable `comp_id` | 400 | `{"error": "invalid request: forms requires ?comp_id="}` | `invalid_request` · forms requires ?comp_id= | ## Not errors Most parameters are clamped to their range rather than rejected, and some malformed input simply finds nothing. These requests all return `200`; check the body, not only the status: | Request | What happens | Status | Body (lists shown as their length, Gurmukhi as ‹Gurmukhi›) | |---|---|--:|---| | `/api/ang/0` | an Ang below 1 is clamped to 1 | 200 | `{"ang": 1, "lines": [23 items], "continued_from": null, "raag": null, "section": ‹Gurmukhi›, "authors": [1 item]}` | | `/api/ang/1431` | an Ang above 1430 is clamped to 1430 | 200 | `{"ang": 1430, "lines": [57 items], "continued_from": 1429, "raag": null, "section": ‹Gurmukhi›, "authors": [1 item]}` | | `/api/search?q=naam&limit=999` | `limit` above 200 is clamped to 200 | 200 | `{"mode": "theme", "concept": {…}, "results": [200 items], "related_themes": [0 items]}` | | `/api/lines?ids=abc` | ids that are not digits are ignored | 200 | `{"lines": [0 items], "note": "verbatim Gurmukhi by line id, cited by Ang"}` | | `/api/analytics/vaar?id=x` | a Vaar id that is not a number finds no Vaar | 200 | `{"vaar": null, "units": [0 items]}` | | `/api/word` | no word: nothing to count | 200 | `{"word": "", "count": 0, "lines": [0 items]}` | | `/api/neighbors` | no `line_id`: no neighbours | 200 | `{"line_id": 0, "level": "none", "neighbors": [0 items]}` | | `/api/timing/raag?name=nope` | an unknown raag name: no claims | 200 | `{"available": true, "raag": "nope", "claims": [0 items], "note": "no such raag"}` | ## Beyond a single request - **`404` from a single-context service.** A service that runs one context answers a route it does not own with `404` and the message `/api/ is not served by this service`. Through the public origin the gateway always sends a route to its own service, so a client only sees this when calling a function directly ([bounded contexts and the gateway](https://docs.gurbanisoul.com/architecture/bounded-contexts-and-gateway/)). - **`503` from `/readyz`.** `{"ready": false, …}` when a declared table is missing, or `{"ready": false, "error": "database unavailable"}`. `/healthz` is liveness only. - **The platform in front.** Vercel may answer before the API does, for example a timeout or a challenge page for an unusual burst of requests. Such responses are not JSON from this API and carry no `X-Service` header.