Problem types
Every RFC 9457 problem type the API can answer with, generated from AlvoProblemTypes.
Every refusal is an RFC 9457 problem document whose type is one of the URIs below. Branch on the slug, never on detail, which is prose.
The type URIs use the namespace https://alvo.dev/errors/, which does not resolve yet. The slug after the last / is the anchor on this page: https://alvo.dev/errors/forbidden is #forbidden.
validation
Section titled “validation”Status: 422 · type: https://alvo.dev/errors/validation · Returned by: every host
Schema-derived validation refused the request body.
Causes
- A field the entity declares
requiredis missing or null. - A value breaks a declared facet:
maxLength,enum,format,precisionorscale. - The body names a field the entity does not declare, writes a read-only field, or points a
refat a row that does not exist. - The body is not a JSON object, is not valid JSON, repeats a field, or is larger, deeper or wider than the configured limits.
- A batch body has no
rowsarray, an empty one, too many rows, or a row without a validid. - A Management API apply carried a descriptor that does not validate.
Fix: Correct each field the violations point at; every violation carries a pointer and a fixSuggestion.
Violation codes: required, max-length, enum-value, format, format-not-evaluated, precision, scale, unknown-field, invalid-value, read-only-field, read-only-required-field, unresolved-reference, not-an-object, malformed-json, duplicate-field, body-too-large, body-too-deep, body-too-many-fields, not-a-batch, empty-batch, batch-too-many-rows, invalid-row-id, descriptor
Guides: Write data safely · Entities and fields · Handle errors
malformed-query
Section titled “malformed-query”Status: 422 · type: https://alvo.dev/errors/malformed-query · Returned by: every host
The query string or the request body is malformed — the shape is wrong, nothing is hidden.
Causes
- A
filter,order,select,limit,offsetorafterparameter is malformed, repeated or contradicts another. - A filter names a field that is unavailable to the caller, or uses an operator the field’s type does not support.
- A filter is deeper, wider or lists more
incandidates than the limits allow. - A query sent as a request body is not a JSON object, is not valid JSON, names a parameter twice, or is larger, deeper or wider than the limits allow.
Fix: Correct the parameter each violation names in its pointer; the shape is wrong, nothing is hidden.
Violation codes: unavailable-field, unknown-operator, unsupported-operator-for-field, malformed-filter, malformed-filter-group, malformed-in-list, malformed-is-operand, invalid-filter-value, filter-too-deep, filter-too-wide, too-many-in-candidates, filter-beyond-port-limits, pattern-too-long, invalid-page-size, invalid-offset, invalid-cursor, conflicting-paging, malformed-order, repeated-sort-key, malformed-select, malformed-select-alias, colliding-projection-key, projection-too-wide, too-many-select-entries, too-many-query-values, unrepresentable-query-value, repeated-parameter, not-an-object, malformed-json, duplicate-field, body-too-large, body-too-deep, body-too-many-fields
Guides: Read data: filter, sort, page · Handle errors
forbidden
Section titled “forbidden”Status: 403 · type: https://alvo.dev/errors/forbidden · Returned by: every host
A policy refused the operation.
Causes
- The entity’s access rules deny the operation, or the row being written would not satisfy them.
- A before-hook’s
rejectfired, or amutateproduced a value the target field’s facets refuse. - A Management API caller does not reach the project access level the route needs.
- In a batch, rows refused by policy or named twice are listed in
violations, one entry per row.
Fix: Change the request so the rules admit it, or call with a role the rules grant. A rule’s refusal never says which rule refused; a before-hook’s reject puts its own message in detail, written for the caller.
Violation codes: forbidden, duplicate-row
Guides: Access rules · Before-hooks · Computed fields and rollups · Apply and evolve your descriptor · Handle errors
out-of-scope
Section titled “out-of-scope”Status: 403 · type: https://alvo.dev/errors/out-of-scope · Returned by: every host
The presented API key’s scopes do not cover this entity and operation.
Causes
- The presented API key’s scopes do not cover this entity and operation.
Fix: Grant the key the scope it needs, such as *:read or *:write. This is a different fix from forbidden, which is a rule.
Violation codes: none — this refusal carries no itemised reasons
Guides: Authentication and API keys · Handle errors
not-found
Section titled “not-found”Status: 404 · type: https://alvo.dev/errors/not-found · Returned by: every host
The row does not exist, or the caller’s policy excludes it — indistinguishably.
Causes
- The row does not exist, or the caller’s rules exclude it; the two are deliberately indistinguishable.
- On the Management API, the project or revision does not exist.
Fix: Check the id, and check that the caller’s rules let it read the row.
Violation codes: none — this refusal carries no itemised reasons
Guides: Read data: filter, sort, page · Access rules · Handle errors
precondition-failed
Section titled “precondition-failed”Status: 412 · type: https://alvo.dev/errors/precondition-failed · Returned by: every host
The write carried a version the stored row does not have.
Causes
- The
If-Matchversion does not match the stored row: someone else changed it, or your own earlier write already landed. - The request carries a precondition this API cannot evaluate: several or weak tags in
If-Match,If-None-Matchon a write, any precondition on a create, or a version on an entity withoutaudit. - A Management API apply or rollback named a revision that is no longer current.
Fix: Read the row (or the descriptor) again, reapply your change, and resend it with the new ETag in If-Match.
Violation codes: none — this refusal carries no itemised reasons
Guides: Write data safely · Apply and evolve your descriptor
precondition-required
Section titled “precondition-required”Status: 428 · type: https://alvo.dev/errors/precondition-required · Returned by: the Management API only, on any host that maps it — never a Data API route
The write requires a precondition and carried none.
Causes
- A Management API apply or rollback carried no
If-Match.
Fix: Read the current revision and send it as If-Match.
Violation codes: none — this refusal carries no itemised reasons
Guides: Apply and evolve your descriptor
idempotency-conflict
Section titled “idempotency-conflict”Status: 409 · type: https://alvo.dev/errors/idempotency-conflict · Returned by: every host
An idempotency key was reused for a different request.
Causes
- An
Idempotency-Keywas reused for a different request.
Fix: Send a fresh key with a different request; reuse a key only to retry the identical request.
Violation codes: none — this refusal carries no itemised reasons
Guides: Write data safely · Apply and evolve your descriptor
conflict
Section titled “conflict”Status: 409 · type: https://alvo.dev/errors/conflict · Returned by: every host
The request collides with stored state a database constraint guards.
Causes
- A value another record already holds on a
uniquefield. - A delete that a
refdeclaringonDelete: "restrict"refuses, because other records still point at the row.
Fix: Send a value no other record holds, or delete or repoint the records that reference this one, then retry.
Violation codes: unique, referenced
Guides: Write data safely · Entities and fields · Indexes and uniqueness
destructive-change
Section titled “destructive-change”Status: 409 · type: https://alvo.dev/errors/destructive-change · Returned by: the Management API only, on any host that maps it — never a Data API route
The change would discard data and the caller did not ask for that.
Causes
- A Management API apply whose plan would discard data, sent without the destructive allowance.
Fix: Resend with an explicit destructive allowance, or send a descriptor that keeps what the plan would drop.
Violation codes: none — this refusal carries no itemised reasons
Guides: Apply and evolve your descriptor
unauthenticated
Section titled “unauthenticated”Status: 401 · type: https://alvo.dev/errors/unauthenticated · Returned by: every host
A credential was presented and cannot be used.
Causes
- A credential was presented and cannot be used: an unknown, revoked or expired key, a wrong secret, or a key not issued for the requested tenant.
- A request with no credential at all is not refused with this slug: it runs as an anonymous caller, and the rules decide.
Fix: Send a valid key in the header the WWW-Authenticate challenge names, X-Alvo-Api-Key by default.
Violation codes: none — this refusal carries no itemised reasons
Guides: Authentication and API keys · Handle errors
unreadable-request
Section titled “unreadable-request”Status: 400, 408 or 413 · type: https://alvo.dev/errors/unreadable-request · Returned by: the standalone host; an embedded host only with AddAlvoProblemDetails() and UseExceptionHandler()
The server refused the request before Alvo could read it.
Causes
- The web server refused the request before Alvo read it: a body over the server’s request size limit (413), one arriving too slowly (408), or one whose framing broke (400).
Fix: Send a different request: a smaller body, sent in one go, with valid framing.
Violation codes: none — this refusal carries no itemised reasons
Guides: Write data safely · Handle errors
unsupported-media-type
Section titled “unsupported-media-type”Status: 415 · type: https://alvo.dev/errors/unsupported-media-type · Returned by: every host
The request carried a body that is not declared as JSON, or carried no Content-Type at all.
Causes
- A request body was not declared as JSON, or carried no
Content-Typeat all, so nothing read it.
Fix: Send Content-Type: application/json. The fix is the header, not the body.
Violation codes: none — this refusal carries no itemised reasons
Guides: Write data safely · Handle errors
internal
Section titled “internal”Status: 500 · type: https://alvo.dev/errors/internal · Returned by: the standalone host; an embedded host only with AddAlvoProblemDetails() and UseExceptionHandler()
An invariant Alvo itself relies on is broken.
Causes
- An invariant Alvo itself relies on is broken. The answer carries no reason, by design.
Fix: Nothing in the request is at fault. The host’s log has the exception and its stack trace.
Violation codes: none — this refusal carries no itemised reasons
Guides: Handle errors · Running in production
function-failed
Section titled “function-failed”Status: 500 · type: https://alvo.dev/errors/function-failed · Returned by: the standalone host; an embedded host only with AddAlvoProblemDetails() and UseExceptionHandler()
A CEL function failed while the write was evaluated, so nothing was written.
Causes
- A CEL function failed while a write was evaluated: a host function threw, a built-in refused (an overflow, a result too long), or an argument did not fit its parameter’s type. Nothing was written.
Fix: Check the function the detail names and the values the write passes to it; the host’s own exception is in its log, never in the response.
Violation codes: none — this refusal carries no itemised reasons
Guides: Custom CEL functions · Handle errors