Skip to content
Alvo is pre-v0.1: the image runs from its edge tag, and no NuGet package or release is published yet.Pre-v0.1: no release yet.Roadmap and status

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.

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 required is missing or null.
  • A value breaks a declared facet: maxLength, enum, format, precision or scale.
  • The body names a field the entity does not declare, writes a read-only field, or points a ref at 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 rows array, an empty one, too many rows, or a row without a valid id.
  • 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

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, offset or after parameter 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 in candidates 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

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 reject fired, or a mutate produced 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

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

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

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-Match version 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-Match on a write, any precondition on a create, or a version on an entity without audit.
  • 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

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

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-Key was 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

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 unique field.
  • A delete that a ref declaring onDelete: "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

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

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

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

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-Type at 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

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

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