Reference

Errors

OKE codes, gate denials, and subsystem errors — stable numeric codes with causes and fixes.

OKE has two error families: failures are values a Flow returns ({ data: null, error: { code, data } }), while framework errors are thrown for invariant violations (permanent numeric code + cause + fix).

OKE1110  domain table not found — migrations have not been applied.
         → run `oke db migrate` against this environment.
         https://oke.omqkhafi.dev/e/1110

Codes are stable after the domain-range renumber — match them safely across upgrades (see the Unreleased Breaking Changes mapping if you still have a pre-renumber note).

The one rule

Switch on error.code for Flow failures. Catch / match OKE#### only for thrown framework invariants. Do not treat a typed NotFound as an unhandled exception.

Smallest Example

Return a typed failure

on(
  http.get({
    in: z.object({ id: z.string() }),
    errors: { NotFound: z.object({ id: z.string() }) },
  }),
  flow({
    do: async ({ id }, fx) => {
      const [row] = await fx.store(db).select().from(notes).where(eq(notes.id, id));
      if (!row) return fx.fail("NotFound", { id });
      return row;
    },
  }),
);

Handle it on the client

const { data, error } = await api.notes.get({ id });
if (error?.code === "NotFound") {
  // value — not a throw
}

Localized messages

Typed failures and OKE codes ship English and Arabic ICU catalogs. Locale comes from Accept-Language (matched to i18n.locales); fallback is i18n.default.

SurfaceKeysHow it appears
fx.fail / failerrors.{code} · errors.{code}.{reason}Optional error.message on the envelope
Thrown OkeErroroke.{code}.cause · oke.{code}.fixCause + fix lines in the thrown message

Override via defineLocale. Pass fail(code, data, { message }) for a custom string. Custom app codes stay message-less until registered. Full catalogs: i18n.

OKE numeric codes

CodeNameCauseFix
1001undeclared readFlow reads a resource not in effects.readsAdd it to the flow's effects.reads
1002undeclared writeFlow writes a resource not in effects.writesAdd it to the flow's effects.writes
1003undeclared emitFlow emits a signal not in effects.emitsAdd it to the flow's effects.emits
1004undeclared sendFlow sends a template not in effects.sendsAdd it to the flow's effects.sends
1005undeclared askFlow asks a prompt not in effects.asksAdd it to the flow's effects.asks
1006undeclared secretFlow reads a secret not in effects.secretsAdd it to the flow's effects.secrets
1007undeclared callFlow calls a flow not in effects.callsAdd it to the flow's effects.calls
1008undeclared fetchFlow fetches a host not in effects.fetchesAdd the hostname to the flow's effects.fetches
1009undeclared embedFlow embeds with a model not in effects.embedsAdd it to the flow's effects.embeds
1020no effects declaredFlow has no effects and no Manifest to infer fromRun oke build / oke dev, or declare effects
1030adopt barrel staleA src/flows/<unit> folder was not adoptedRun oke dev or oke build to regenerate generated.ts
1040HTTP path unresolvedPathless http.get() never received a file-tree stampImport @/flows/generated, or pass http.get("/…")
1041HTTP route clashTwo HTTP flows share the same method + pathGive each flow a unique method + path
1045HTTP flow unnamedAdopted HTTP flow still has no unit.exportExport from flows/<unit>/ or pass a named flow
1050live exposure dupSame signal, gates, and match on two GET routesChange the gate or path-param filter
1060MCP tool duplicateTwo MCP tool bindings share the same tool nameGive each MCP tool exposure a unique name
1070flow name duplicateTwo Flows share the same Manifest / fx.call nameGive at least one an explicit flow("…") or tree export
1071once-signal multi-flowTwo different Flows bound to the same signal.onceUse signal.broadcast, or bind only one Flow
1072flow unnamedSignal / Clock consumer still has no unit.exportExport from flows/<unit>/ or pass a named flow
1110schema missingDomain table absent in prod — no auto-DDLRun oke db migrate against this environment
1210live resume gapLast-Event-ID is not on the retained tapeReconnect without the cursor; remaining tape replays
1240orphan emitEmit with zero subscribers and optional falseAdd on(signal, …) or declare optional: true
1250signal schemaEmit payload failed the signal's schemaPass a payload that matches schema, or remove it
1605channel schemaSend payload failed the template's schemaFix template data payload or the template schema
1810tenant requiredTenant-scoped op with no fx.tenant.idswitchTenant, signed tid, or tenant header
1820tenant not memberClient-supplied tenant id is not a membershipPick from listTenants or add the user as a member
1830tenant unknown scopeTenant role used an invented or console:* scopeUse a declared application scope

Effects are usually inferred

The 1001–1007 · 1008 · 1009 family exists for flows that declare effects explicitly. Most apps never write an effects block — inference covers them — so seeing one of these means an explicit declaration drifted from the code.

Gate denials (typed failures)

Returned, not thrown — the request never reached do:

CodeWhenPayload
UnauthorizedPolicy denied, request not authenticated
ForbiddenPolicy denied, authenticated but not allowedgate, reason (tenant_required · not_member · unknown_scope · session_only · …)
RateLimitedRate gate budget exhaustedretryAfterMs

Framework validation failures

CodeWhen
ValidationErrorInput failed the in schema, or a list param isn't whitelisted (unknown list param "x")
NotFoundA store.resource get/update/remove hit a missing row
InvalidQueryQUERY missing Content-Type (reason: missing_content_type) or body isn't JSON (inconsistent_content) — 400
UnsupportedMediaTypeQUERY Content-Type is present but not application/json415, Accept-Query lists JSON

Subsystem errors

Thrown by specific subsystems — each names its own cause:

ErrorThrown whenWhat to do
VaultBootErrorA vault contract has no value in any resolution layerSet the missing names — the error lists every gap
VaultError (UNSUPPORTED)Managed provider unknownUse an official id, built-in vault, or env
VaultSealedConsole rotate-master while this process holds no master keyExport OKE_VAULT_MASTER_KEY or run oke vault unseal
VaultRotateBusyMaster-rotation lease held or a batch is already in flightWait and continue, or resume with oke vault rotate-master --new-key
VaultUnsupportedConsole vault action on a non-builtin backend or missing SQLUse drivers.vault = "vault" and set DATABASE_URL
AiPiiBuildErrorBuild: a flow sends PII fields to a third-party modelDrop the fields or add allowPii: true — fields named
AiSchemaValidationErrorA model response failed the prompt's out schemaFix the prompt or the schema — response didn't conform
ScheduleNotOverridableErrorConsole tried to edit a clock declared without overridable: trueDeclare it overridable and redeploy
ClockResourceNotFoundErrorConsole action targeted an unknown clock nameCheck the name against your clock() declarations
DryRunWriteIsolationErrorA write attempted inside a dry runDry runs never write — use a real run
ManifestValidationErrorThe compiled Manifest failed schema validationRe-run the build; the error names the offending entry
CrossPlaneErrorA user-plane token was used on the operator plane (or vice versa)Use the correct principal for the plane
AttenuationErrorA token was used beyond its attenuated scopeRe-issue with the needed scope
SessionErrorSession token invalid, expired, or malformedRe-authenticate
OperatorErrorOperator-plane operation failed its checksError message names the failed check
AccessGrantErrorConsole access grant rejectedRe-request access with a valid grant
UnsupportedPathErrorA route path shape the router can't compileSimplify the path pattern

Troubleshooting

Learn more

  • i18n — catalogs, fx.t, locale matching
  • Flowfx.fail and the response envelope
  • Gate — where the three denials come from
  • CLIoke db migrate and friends
  • fx — how failures and effects surface

Next

On this page