Skip to content

Diagnostics

Every failure in Mantle is a Diagnostic: one structured object with a stable code, the phase that produced it and a JSON Pointer to the offending place. The catalog is closed — adding a code is a grammar-revise event — so an agent can group by code or filter by phase without parsing prose. This page is the whole catalog. The rules that raise each code live on the atom pages: Manifest, Schema, View, Procedure, Trigger, Authorization and Site config.

Shape

ts
interface Diagnostic {
  readonly code: DiagnosticCode;
  readonly phase: "validate" | "test" | "boot" | "runtime";
  readonly severity: "error" | "warning";
  readonly path: string;
  readonly source?: SourceLocation;
  readonly value?: unknown;
  readonly expected?: string;
  readonly candidates?: readonly string[];
  readonly suggestion?: string;
  readonly message: string;
  readonly failure?: {
    readonly outcome: "not-applied" | "partial" | "unknown";
    readonly retry: "never" | "after-change" | "safe" | "reconcile";
    readonly resource?: string;
  };
}
FieldMeaning
codeOne of the codes below. Unprefixed UPPER_SNAKE.
phaseWhich loop produced it. The same code may appear in more than one phase when it names the same root cause.
severityerror withholds the result of its stage; warning does not.
pathWhere the problem is. In validate and boot phases a manifest path such as manifest:Procedure/expire-order#/spec/handler/ref; in the runtime phase a target path plus a JSON Pointer, such as manifest:View/my-orders#/params/locale.
sourceAuthored location: { sourceId, documentIndex, path } plus a line and column span when the YAML node is known. Present on parse diagnostics.
valueThe offending value, when one can be shown.
expectedWhat would have been accepted, in prose.
candidatesThe valid alternatives — declared handler keys, enabled locales, declared Procedure names. Security-sensitive.
suggestionNearest-match hint, when one is computed.
messageHuman-readable. Call sites may supply their own; otherwise it is derived as [<phase>/<code>] at <path>; expected <…>; got <…>; (did you mean <…>?). The structured fields stay authoritative.

candidates is stripped by redactForWire before any HTTP egress, because listing valid alternatives to an untrusted caller leaks schema information. Internal phases — validate, test and boot — skip that redaction, so a CLI or boot log keeps the full list. One or more diagnostics travel across a transport boundary inside a DiagnosticError; the boundary catch emits the structured payload instead of falling back to the INTERNAL_ERROR envelope reserved for genuinely unexpected throws.

failure describes safe effect and recovery facts for storage and service failures. safe means retrying the same idempotent operation is safe, not that Mantle automatically retries it. Unknown write/send outcomes require reconciliation unless the adapter guarantees idempotence. Provider payloads, SQL and credentials belong in DiagnosticError’s internal cause, never public fields. See the port operation matrix.

Validate-only

Raised by the parser, the graph validator and the code generator. mantle validate and mantle generate surface these; parsing is all-or-nothing, so one error-severity diagnostic withholds the whole parsed set.

CodeMeaningHTTP
INVALID_MANIFEST_ENVELOPEWrong apiVersion, unknown key at a known level, wrong value shape, a YAML syntax or alias-limit failure, or a Schema data property named expectedVersion (reserved Procedure OCC token; ADR-0022).
DUPLICATE_NAMETwo documents of the same kind declare the same metadata.name.
VIEW_FROM_UNKNOWN_SCHEMAspec.from names no declared Schema.
VIEW_FIELD_NOT_IN_SCHEMAA fields entry or orderBy[i].field is neither a Schema property nor a reserved entry column.
VIEW_FILTER_FIELD_NOT_IN_SCHEMAA filter comparison names an unknown field.
VIEW_PARAMS_INVALID_SHAPEspec.params is not an object schema with an object properties.
VIEW_PARAMS_RESERVED_NAMEparams.properties declares page, show or cursor.
VIEW_FILTER_PARAM_REF_UNKNOWN{ $param: <name> } names a param that is not declared.
VIEW_FILTER_PARAM_REF_NOT_REQUIREDThe referenced param is not listed in params.required.
VIEW_FILTER_CTX_USER_REF_INVALIDThe { "$ctx.user": "id" } sentinel is malformed or used outside eq.
VIEW_FILTER_CTX_USER_REF_REQUIRES_AUTHThe sentinel is used without ctx.user in requires.auth.all.
VIEW_FILTER_CTX_USER_REF_REQUIRES_INDEXThe bound field is not the leftmost field of a declared index.
VIEW_ORDERBY_INVALIDAn orderBy entry has the wrong shape or an unknown direction.
VIEW_UI_INVALIDA View uiSchema key is unknown, used on a public View, or names an unknown output field.
REQUIRED_FIELD_UNKNOWNA required entry of spec.schema is not declared under properties.
INVALID_PATTERNA pattern does not compile as a JavaScript regular expression.
JSON_SCHEMA_UNSUPPORTEDA JSON Schema keyword outside the accepted subset.
JSON_SCHEMA_REF_INVALIDA $ref does not begin #/$defs/ or does not resolve in the same document.
JSON_SCHEMA_LIMIT_EXCEEDEDNesting deeper than 100 levels, or more than 10,000 schema nodes.
BIND_VALUE_NOT_IN_ENUMx-mantle-bind is not ctx.user, ctx.staff or now.
AUTH_PREDICATE_NOT_IN_ENUMA ctx.staff role is not owner, editor or contributor.
GUARD_PROCEDURE_UNKNOWNrequires.guard.procedure names no declared Procedure.
GUARD_SELF_REFERENCEA Procedure names itself as its own guard.
GUARD_PROCEDURE_BUILTINThe guard Procedure uses a builtin handler instead of handler.kind: ref.
GUARD_CHAIN_NOT_ALLOWEDThe guard Procedure itself declares a guard.
SCHEMA_INDEX_INVALIDAn index tuple breaks a shape, naming, reserved-column, type or duplication rule.
SCHEMA_INDEX_FIELD_UNKNOWNAn indexes field is not a top-level Schema property.
UNIQUE_INDEX_FIELD_UNKNOWNA uniqueIndexes field is not a top-level Schema property.
SCHEMA_SEARCH_INVALIDsearchableFields repeats an entry or names a non-string property.
SCHEMA_SEARCH_FIELD_UNKNOWNA searchableFields entry is not a Schema property.
SCHEMA_UI_INVALIDA Schema or Procedure uiSchema rule is broken, including unknown roots, invalid nav.standalone, or a Schema declaring uiSchema.collectionAction.
HANDLER_BUILTIN_NOT_IN_V010A builtin Procedure was invoked on a runtime assembled without the builtin collaborator.
MANIFEST_ROOT_NOT_FOUNDThe manifests directory is missing, unreadable, or contains no .yaml or .yml file.
MANIFEST_READ_FAILEDA manifest source could not be read.
CODEGEN_IDENTIFIER_COLLISIONTwo names in one group collapse to the same generated lower-camel identifier.
FIXTURE_SCHEMA_VIOLATIONReserved for consumer-authored test diagnostics on the test phase. Core emits purpose-shaped harness reports instead.

Cross-phase

Named by the same code in validate, boot or runtime, depending on where the condition is detectable.

CodeMeaningHTTP
HANDLER_NOT_REGISTEREDA handler.kind: ref key has no function in the handlers map. Boot attaches the registered keys as candidates; the runtime occurrence is defence in depth.500
TRIGGER_TARGET_PROCEDURE_UNKNOWNspec.target.procedure names no declared Procedure.
TRIGGER_PATH_COLLISIONTwo HTTP Triggers claim the same (method, path).
TRIGGER_PATH_INVALIDAn HTTP Trigger path does not start /api/ (validate), or falls under an adapter-reserved prefix (boot).
MCP_TOOL_NAME_COLLISIONTwo atoms mangle to the same MCP tool name, a Procedure takes a reserved generic name or prefix, or two MCP Triggers share a (surface, tool name).
PROCEDURE_NOT_FOUNDAn invocation names a Procedure that is not in the compiled plan.
NOT_FOUNDThe addressed resource does not exist: an entry id, a View name, a media asset, an operation name.404
METHOD_NOT_ALLOWEDThe path exists but the method is not bound.405
VIEW_DIALECT_UNSUPPORTEDThe bound storage adapter does not support a sql View's dialect.

Builtin handlers and lifecycle

CodeMeaningHTTP
BUILTIN_HANDLER_SCHEMA_UNKNOWNhandler.schema names no declared Schema.
BUILTIN_HANDLER_CONTRACT_INVALIDThe Procedure's input breaks the builtin op's contract, such as a missing expectedVersion on update or a match tuple that is not exactly one uniqueIndexes entry.
LIFECYCLE_SCHEMA_UNKNOWNA lifecycle Trigger's source.schema names no declared Schema.
LIFECYCLE_HOOK_REJECTEDA before_* hook aborted the mutation. The diagnostic names the rejecting hook.409

Locale and translates

CodeMeaningHTTP
SCHEMA_LOCALIZED_REQUIRES_SITE_LOCALESA Schema declares localized: true while the site has no valid locales.
TRANSLATES_PARENT_UNKNOWNtranslates.parent names no declared Schema.409
TRANSLATES_REQUIRES_LOCALIZEDA Schema declares translates without localized: true.
TRANSLATES_REQUIRES_CONTENT_FIELDA translation child declares no property besides locale and the join field.
TRANSLATES_FIELD_NOT_IN_PARENTThe join field is not declared in the parent's properties.
TRANSLATES_FIELD_NOT_IN_CHILDThe join field is not declared in the child's own properties.
TRANSLATES_PARENT_IS_LOCALIZEDThe named parent is itself localized: true.

Runtime

These are the codes that reach a caller. Everything else in this catalog is caught before traffic.

CodeMeaningHTTP
RESOURCE_EXHAUSTEDA known capacity or quota refusal.507
RESOURCE_UNAVAILABLEA required storage or service dependency is unavailable.503
RATE_LIMITEDA recognized request-rate refusal.429
OUTCOME_UNKNOWNThe operation may have taken effect; reconcile before retry.503
PARTIAL_FAILURESome effects completed; inspect the operation’s recovery contract.503
PRECONDITION_FAILEDA storage or service precondition failed.412
INPUT_VALIDATION_FAILEDProcedure input, View params or an entry's data failed the compiled schema, including the write-time locale gate.400
INVALID_LOCALEA locale value is not a canonical Mantle v0.1 tag. Also raised at boot against site_config/locales.400
UNAUTHENTICATEDAn auth predicate failed and the caller presented no identity at all.401
ENTITLEMENT_REQUIREDA guard Procedure denied the call on current business state.402
AUTH_DENIEDAn auth predicate failed for a caller that is authenticated in some way, or an Admin caller lacks the required staff role.403
CONFLICTOptimistic-concurrency mismatch, unique-index violation, an illegal lifecycle transition, or a generic write against a read-only Schema. Not retryable as sent.409
OUTPUT_VALIDATION_FAILEDA handler returned a value that does not match its declared output. A handler bug.500
INTERNAL_ERRORAn uncaught handler exception.500
DISPATCHER_NOT_BUILTThe runtime feature is not implemented in this build.501
MEDIA_NOT_CONFIGUREDMedia uploads are not enabled: no mediaStorage port is bound.501
MEDIA_PURPOSE_REJECTEDThe requested purpose is not declared in siteDefaults.media.purposes. The declared set is returned in expected.400
MEDIA_MIME_REJECTEDThe declared mime is outside the accepted image set.400
MEDIA_SVG_REJECTEDAn SVG upload was attempted while the adapter has SVG disabled; object storage does not sanitize SVG payloads.400
MEDIA_SIZE_EXCEEDEDA declared byteSize exceeds the cap.400
MEDIA_VARIANTS_INCOMPLETEThe variants manifest does not cover every mime the purpose requires.400
MEDIA_VARIANT_SIZE_EXCEEDEDOne variant's byteSize exceeds its mime's maxBytes.400
MEDIA_VARIANTS_SUSPICIOUS_SIZEA modern format is larger than its fallback, so the uploader skipped optimization for that variant.400
MEDIA_UPLOAD_EXPIREDThe upload capability's TTL elapsed, or it never existed.410
MEDIA_OBJECT_NOT_FOUNDCommit ran before every variant's bytes reached the storage backend.409
MEDIA_CHECKSUM_MISMATCHUploaded bytes do not match the declared checksum.409
MEDIA_ASSET_NOT_FOUNDNo media_assets row matches the id.404

How diagnostics surface

Only the runtime phase maps to HTTP. httpStatusFor reads the status table above; a runtime code that is not in that table becomes 500. Validate, test and boot phases have no HTTP mapping at all — they surface through:

  • CLI exit codes. mantle validate, mantle generate and mantle-harness print each diagnostic as <code> <path>: <message> in text mode or as JSON otherwise. Exit 0 means no errors (warnings are allowed), 1 means one or more errors, 2 means a CLI invocation problem.
  • A thrown BootValidationError. Deployment preparation collects boot diagnostics and throws them together; the error carries the full diagnostics array.
  • Worker init logs. On Cloudflare the facade boots the runtime once per isolate. A boot failure is logged and the request boundary returns a redacted { ok: false, error: "internal_error" } with 500, so the diagnostic detail stays in the log rather than on the wire.

Five codes are declared in the catalog but not emitted anywhere in the shipped source at this version: FIXTURE_SCHEMA_VIOLATION (reserved for consumer tests by design), MANIFEST_READ_FAILED, METHOD_NOT_ALLOWED, DISPATCHER_NOT_BUILT and MEDIA_CHECKSUM_MISMATCH. They remain part of the public contract because the catalog, not the current set of throw sites, is the contract.

Source