Skip to content

Errors

Every error is an RFC 9457 problem document with a stable, namespaced code. Match on the code, not the HTTP status or the prose. The code is the contract and never changes within v1.

json
{
  "type": "https://opaline.dev/problems/pack.lint_failed",
  "title": "pack.lint_failed",
  "status": 422,
  "code": "pack.lint_failed",
  "detail": "pack.hard_without_citation"
}

The Content-Type is application/problem+json. The SDK surfaces this as a typed error with code, title, and detail.

Idempotency

CodeStatusWhen
idempotency.key_required400decide was called without an Idempotency-Key header.
idempotency.payload_mismatch422The same key was reused with a different body. Use a new key, or resend the original body.
idempotency.in_flight409The same key is still being processed. Retry after Retry-After.

Payload

All four guard the determinism boundary: send numbers as decimal strings.

CodeStatusWhen
payload.float_rejected400A JSON float appeared in a hash-input position. Send it as a string ("0.1").
payload.integer_out_of_range400An integer outside ±(2⁵³−1). Send it as a string.
payload.duplicate_key400A JSON object had a duplicate key.
payload.malformed_json400The body is not valid JSON.
payload.too_large413The request exceeds the size cap.

Auth

CodeStatusWhen
auth.invalid_key401The key is missing, malformed, revoked, or wrong for the plane.
auth.scope_missing403The key is valid but lacks the scope the operation needs.

Packs

CodeStatusWhen
pack.not_found404Unknown pack id, version, or alias.
pack.invalid400The pack document is malformed or structurally invalid.
pack.lint_failed422A lint rule failed (e.g. a hard rule without a citation). detail names the rule.
pack.embedded_test_failed422An embedded test vector failed during the publish gate.
pack.version_immutable409Re-publishing an existing id@version with different content.

Rate & quota

CodeStatusWhen
rate.limited429The per-tenant rate limit was exceeded. Back off per Retry-After.
quota.exceeded429The tenant's monthly decide quota is exhausted.

Request & service

CodeStatusWhen
request.rejected400/413The request was rejected by a structural or size guard.
tenant.not_found404Unknown tenant (internal provisioning surface).
simulate.snapshots_unavailable422Trail-history simulate needs a snapshot-retention tenant.
service.unavailable503Fail-closed: the engine refused to decide without a durable trail. Safe to retry.

Retrying

  • Safe to retry (transient): idempotency.in_flight, rate.limited, service.unavailable. Honour Retry-After where present.
  • Do not retry unchanged (deterministic): everything else. Fix the request, the key, the scope, or the pack first.

Because decide is idempotent, retrying a timed-out request with the sameIdempotency-Key and body is always safe: it replays the stored response rather than deciding twice.