---
url: /reference/error-codes.md
description: >-
  The framework's error codes with their HTTP statuses, and the codes of
  rejected queries.
---

# Error Codes

Every code the framework answers a request with, as `code` of the `application/problem+json` body and in a command's result, generated from [`FrameworkErrors`](https://github.com/Ahoo-Wang/Wow/blob/main/api/manifest/src/main/kotlin/me/ahoo/wow/manifest/FrameworkErrors.kt). A domain rejection's code is the domain's own; it is answered with 422 unless the host sets another status for it (`wow.http.errors.[<code>].status`). With `If-Match`, `wow.aggregate.version-mismatch` is answered with 412.

## Framework codes

| Code | Status | Meaning |
| --- | --- | --- |
| `wow.auth.unauthenticated` | 401 | The request carries no valid credentials. |
| `wow.auth.forbidden` | 403 | The caller may not send this command. |
| `wow.aggregate.forbidden` | 403 | The aggregate is outside the caller's scope. |
| `wow.command.malformed` | 400 | The command body cannot be read as the command type. |
| `wow.command.invalid` | 400 | The command failed validation; the problem lists the violations. |
| `wow.wait.invalid` | 400 | The wait is not a chain the runtime can follow; the command was not run. |
| `wow.command.unhandled` | 400 | The aggregate does not handle this command. |
| `wow.aggregate.not-found` | 404 | The aggregate does not exist. |
| `wow.aggregate.exists` | 409 | The aggregate already exists. |
| `wow.aggregate.version-mismatch` | 409 | The aggregate is not at the expected version (412 when the request carried If-Match). |
| `wow.command.request-id-reused` | 409 | The request id was used by another command. |
| `wow.command.contended` | 409 | Concurrent writers kept winning the version; retrying with the same request id is safe. |
| `wow.aggregate.deleted` | 410 | The aggregate is deleted; only RestoreAggregate applies. |
| `wow.command.busy` | 429 | The command was not taken: its aggregate has too many commands waiting, or the node too many in flight; retry later. |
| `wow.command.unknown` | 503 | Whether the command committed is unknown; retrying with the same request id is safe. |
| `wow.command.failed` | 500 | The command's handling failed with an unexpected error; it committed nothing, and a retry fails alike. |
| `wow.node.closing` | 503 | The node is shutting down; retry on another node. |
| `wow.node.standby` | 503 | The node is on standby until the cut-over: it serves reads only. |
| `wow.storage.unavailable` | 503 | A store could not be reached or did not answer in time; retry later, a command with the same request id. |
| `wow.schedule.not-due` | 503 | A scheduled command arrived too long before its due time on this node's clock; it is sent again later. |
| `wow.schedule.job-invalid` | 400 | The job command does not fit its job or instance: an unknown job, another scope, or an instance not enabled. |
| `wow.subscription.parked` | 500 | A subscription on the wait chain parked the aggregate; the wait cannot be reached until it resumes. |
| `wow.wait.timeout` | 504 | The wait ran out of time; the command may have committed. The problem carries the last stage reached. |
| `wow.query.malformed` | 400 | The query body cannot be read as a query. |
| `wow.query.invalid` | 400 | The query failed admission; the problem lists the violations, each with its code (UNKNOWN\_FIELD, ...). |
| `wow.query.failed` | 500 | The query backend failed to run the query. |
| `wow.query.not-found` | 404 | A single query matched no record: a query result, not a statement that an aggregate does not exist. |
| `wow.query.dataset-not-found` | 404 | No dataset of that name is served to the caller. |
| `wow.query.timeout` | 504 | The query ran longer than its budget allows; narrow it rather than run it again (decision D254 ③). |
| `wow.query.min-version-not-reached` | 409 | The state read asked for a version the aggregate has not reached within the wait; the problem carries its current version: retry later. |
| `wow.query.busy` | 429 | The query was not run: its store has as many queries running as it allows; retry later. |
| `wow.query.dataset-unavailable` | 409 | The dataset is served but cannot be queried now, for the reason the problem names (it needs revision). |
| `wow.call.command-forbidden` | 403 | The calling service may not call this command: its wow.http.services.\<service>.commands do not list it. |

## Query request codes

A rejected query request (`wow.query.malformed` or `wow.query.invalid`) lists what it rejects, each with one of these codes and the part of the request it names, generated from [`QueryErrorCodes`](https://github.com/Ahoo-Wang/Wow/blob/main/api/query-api/src/main/kotlin/me/ahoo/wow/api/query/QueryErrorCodes.kt). Codes are added, never renamed or reused.

| Code | Group |
| --- | --- |
| `INVALID_JSON` | Reading and decoding the request body. |
| `BODY_NOT_OBJECT` | Reading and decoding the request body. |
| `EMPTY_BODY` | Reading and decoding the request body. |
| `UNKNOWN_PROPERTY` | Reading and decoding the request body. |
| `UNKNOWN_TYPE` | Reading and decoding the request body. |
| `UNKNOWN_VALUE` | Reading and decoding the request body. |
| `INVALID_VALUE` | Reading and decoding the request body. |
| `INVALID_REQUEST` | Reading and decoding the request body. |
| `CURSOR_SORT_DUPLICATE` | Reading and decoding the request body. |
| `CURSOR_SORT_TOO_MANY` | Reading and decoding the request body. |
| `INVALID_CURSOR` | Reading and decoding the request body. |
| `SIZE_OUT_OF_RANGE` | The entry budget and gates (errorCode IllegalArgument). |
| `FILTER_TOO_LARGE` | The entry budget and gates (errorCode IllegalArgument). |
| `EXPENSIVE_OPERATOR_DISABLED` | The entry budget and gates (errorCode IllegalArgument). |
| `COUNT_REQUIRES_FILTER` | The entry budget and gates (errorCode IllegalArgument). |
| `RESIDUAL_GROUPS_EXCEEDED` | The entry budget and gates (errorCode IllegalArgument). |
| `EXPLICIT_ENTRY_REQUIRED` | The entry budget and gates (errorCode IllegalArgument). |
| `REPLAY_TOO_LONG` | A state point read (GET …/{id}/state?version=|at=, …/state/trace). |
| `UNKNOWN_FIELD` | Admission against the query model. |
| `UNSUPPORTED_CAPABILITY` | Admission against the query model. |
| `ELEMENT_SCOPE_REQUIRED` | Admission against the query model. |
| `VALUE_MISMATCH` | Admission against the query model. |
| `NOT_COLLECTION` | Admission against the query model. |
| `NOT_SINGLE_STRING` | Admission against the query model. |
| `MODEL_SEARCH_UNSUPPORTED` | Admission against the query model. |
| `CURSOR_NOT_ALLOWED` | Admission against the query model. |
| `PROTECTED_AGGREGATION` | Admission against the query model. |
| `PROTECTED_COMPARISON` | Admission against the query model. |
| `MISSING_KEY_REQUIRES_STRING` | Admission against the query model. |
| `ANY_REQUIRES_SINGLE_VALUE` | Admission against the query model. |
| `INCOMPLETE_PROJECTION` | Admission against the query model. |
| `METRIC_FILTER_SEARCH` | Admission against the query model. |
| `METRIC_FILTER_ELEMENT_MATCH` | Admission against the query model. |
| `METRIC_FILTER_ARRAY_FIELD` | Admission against the query model. |
| `NOT_PROJECTABLE` | Admission against the query model. |
| `EVENT_PROJECTION_TYPE_REQUIRED` | Admission against the query model. |
| `TEMPORAL_REPRESENTATION_REQUIRED` | Admission against the query model. |
| `TEMPORAL_CONFIGURATION_CONFLICT` | Admission against the query model. |
| `PARALLEL_ARRAY_SORT` | Admission against the query model. |
| `ARRAY_EQUALITY` | Admission against the query model. |
| `FIRST_LAST_REQUIRES_SINGLE_VALUE` | Admission against the query model. |
| `FIRST_LAST_REQUIRES_ORDER_BY` | Admission against the query model. |
| `TEMPORAL_AGGREGATION_UNSUPPORTED` | Admission against the query model. |
| `SORT_TOO_MANY` | Admission against the query model. |
| `SORT_FIELD_DUPLICATE` | Admission against the query model. |
| `IDENTITY_UNDEFINED` | Admission against the query model. |
| `STORAGE_UNSUPPORTED` | Admission against the query model. |
| `PREVIOUS_UNSHIFTABLE` | Admission against the query model. |
