Errors and problem details
The MainSDK error shape - RFC 9457 problem details, the MainError class, stable MAIN- codes, redaction, mainsdk explain and MAINSDK_DEBUG.
Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.
Stability The promise an interface ships with.
- Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
- Beta Usable in production; may change before it becomes stable.
- Experimental May change or go away; needs an explicit opt-in in main.yaml.
Every error MainSDK raises tells you, or your agent, what failed, where in your own code, which main.yaml rule was involved and how to fix it. Errors follow open standards so any tool can read them:
| Standard | Used for |
|---|---|
| RFC 9457 Problem Details | Every API error body, Content-Type: application/problem+json |
| OpenTelemetry logs data model and semantic conventions | Every log record, exception attributes and code location |
| W3C Trace Context | trace_id links an error to its request, job and agent change |
| RFC 3339 | Timestamps, in UTC |
The problem body
{
"type": "https://mainsdk.dev/errors/MAIN-DATA-0007",
"title": "Record belongs to another user",
"status": 403,
"detail": "orders.get was called for an order owned by a different user.",
"instance": "/api/orders/8812",
"code": "MAIN-DATA-0007",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"rule": "data.owner_scope",
"fix": "Scope the query with db.orders.for(user).",
"retryable": false
}| Field | Standard or extension | Meaning |
|---|---|---|
type | RFC 9457 | The docs page for the code; it always resolves |
title | RFC 9457 | Short summary, the same for every occurrence of the code |
status | RFC 9457 | HTTP status |
detail | RFC 9457 | What happened this time, redacted |
instance | RFC 9457 | The request path |
code | MainSDK | Stable code, MAIN-<AREA>-<NNNN> |
trace_id | MainSDK | The W3C trace ID from the request's traceparent |
rule | MainSDK | The main.yaml or policy rule involved, when there is one |
fix | MainSDK | What to change, in one sentence |
retryable | MainSDK | Whether retrying can help |
function, file, line | MainSDK | Where in your code; in logs always, in response bodies only outside production |
In production, response bodies sent to end users never carry file, line, function or a stack trace. Those stay in your logs.
Throw a MainError
MainError builds the body for you. Codes are a generated union type, so a typo doesn't compile.
import { MainError } from "mainsdk";
throw new MainError("MAIN-DATA-0007", {
rule: "data.owner_scope",
fix: "Scope the query with db.orders.for(user).",
});Inside a route wrapped by an adapter, such as withMainSDK from mainsdk/next, the thrown error becomes the problem body above, with status 403 and the request's trace_id. An unknown error, such as a TypeError, becomes MAIN-CORE-0500 with no internal detail in production. Outside an adapter, call err.toProblem(instance) yourself.
// mainsdk/log
class MainError extends Error {
constructor(code: ErrorCode, opts?: { detail?: string; fix?: string; cause?: unknown;
rule?: string; retryable?: boolean; status?: number });
readonly code: ErrorCode; // "MAIN-<AREA>-<NNNN>"
toProblem(instance?: string): Problem;
}
interface Problem { // application/problem+json, RFC 9457
type: string; title: string; status: number; detail: string; instance?: string;
code: ErrorCode; trace_id?: string; function?: string; file?: string; line?: number;
rule?: string; fix?: string; retryable: boolean;
}The same error in your logs
Each error is also a log record with OpenTelemetry exception attributes and code location, linked to its request by the trace ID, so your agent can find the cause without guessing:
{
"timestamp": "2026-11-03T10:14:02Z",
"severity": "ERROR",
"body": "Record belongs to another user",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"attributes": {
"exception.type": "MainError",
"exception.message": "orders.get was called for an order owned by a different user.",
"code.function.name": "getOrder",
"code.file.path": "app/api/orders/[id]/route.ts",
"code.line.number": 18
}
}The exception.* and code.* names are OpenTelemetry's. The attribute names for MainSDK's own fields (code, rule) come with the telemetry@1 contract.
Stack frames point at your source, not at SDK internals, through the source maps shipped with every package.
What is redacted
Before an error is written, returned or exported, MainSDK replaces:
- values of fields labelled
personal,sensitive,childorphiwith[redacted:personal]and so on; - anything resolved from a
secret:reference, and known key formats such asmsk_live_…, with[redacted:secret].
Codes
| Part | Rule |
|---|---|
MAIN | Fixed prefix |
<AREA> | One of AUTH, ACCESS, DATA, CONFIG, SECRET, POLICY, NET, LOG, CORE, CONTRACT, EMAIL, PAY, FILES, WEBHOOK, JOBS, AI, SCHEMA, API, IMPORT, CONN, SEED, PREVIEW, ENGAGE, ANALYTICS, METER, LIMIT, COMPUTE, MAIL, CAL, SHARE, LINK, DOC, SANDBOX, I18N, VOICE, EVAL, and later interfaces |
<NNNN> | Four digits, assigned once, never reused or changed in meaning |
Every code is declared once in the contract source with its title, HTTP status, default fix, retryable flag and docs text. Removing a code, or changing what it means, is a breaking change that mainsdk contract diff fails. Every code defined so far, including areas other specs add (such as KEYS, PRIV, NOTIFY, MCP, REALTIME and HOOK), is listed in check codes.
Runtime errors from seal and apps
These runtime codes come from the build seal (spec seal) and the app registry (spec app-registry). Every other code is in check codes.
| Code | Meaning |
|---|---|
MAIN-SEAL-0001 | The files on disk don't match the sealed build hash. Logged in warn mode; the app refuses to start in refuse mode |
MAIN-SEAL-0002 | No seal, or an unreadable one, outside dev while seal.mode isn't off |
MAIN-APPS-0001 | The registry folder is unsafe or not writable; nothing is written and the app keeps serving |
MAIN-APPS-0006 | A peer call between apps that don't both list each other under apps.peers |
MAIN-APPS-0007 | A peer call to an action the callee doesn't list under apps.expose |
MAIN-APPS-0008 | The link handshake failed: the peer's proof doesn't verify, or its seal is invalid |
MAIN-APPS-0009 | apps.link: on without apps.identity and MAINSDK_APP_KEY; the link stays off |
Runtime errors from memory
These runtime codes come from agent memory (spec memory, planned for 0.4). The check findings MAIN-MEM-0020 to MAIN-MEM-0025 are in check codes.
| Code | Meaning |
|---|---|
MAIN-MEM-0001 | No subject: about is missing and the call has no on_behalf_of or session user (400) |
MAIN-MEM-0002 | No memory with that id in the current tenant, app and scope (404, same body either way) |
MAIN-MEM-0003 | The injection check flagged the text of a write; nothing was stored (422) |
MAIN-MEM-0004 | A recalled memory was flagged by the injection check and left out of the context (logged) |
MAIN-MEM-0005 | The agent has no memory grant for this verb, or forget: deny (403) |
MAIN-MEM-0006 | The agent is over its memory recalls or writes per day (429) |
MAIN-MEM-0007 | Forget refused: a legal hold applies to the person (409) |
MAIN-MEM-0008 | A children's rule refused it: children are off, no guardian consent, or a provider model would extract (403) |
MAIN-MEM-0009 | Invalid input: unknown kind, bad duration, pan label, k over 100, hops over 2 or a bad predicate (400) |
MAIN-MEM-0010 | Revise of a version that is no longer current (409) |
MAIN-MEM-0011 | Import refused: a malformed line, or personal or non-procedure memory crossing a tenant or app (422) |
MAIN-MEM-0012 | The driver doesn't declare the capability the call needs (501) |
MAIN-MEM-0013 | The memory store is unreachable (503, retryable); context degrades to an empty block |
MAIN-MEM-0014 | An Anthropic memory tool path is outside /memories/ or tries to traverse (400) |
MAIN-MEM-0015 | A driver hasn't confirmed the erase yet; no receipt is issued and the erase keeps retrying (503) |
MAIN-MEM-0016 | A write from a tool, MCP, the web or an import was refused by from-tools: refuse (403) |
Runtime errors from the API and the typed client
These runtime codes come from the specs app-api. Each one also has a row in check codes.
| Code | Meaning |
|---|
Runtime errors from imports, connections and MCP
These runtime codes come from the specs imports, connections. Each one also has a row in check codes.
| Code | Meaning |
|---|
Runtime errors from seed, preview, analytics and engagement
These runtime codes come from the specs seed-preview, analytics, engagement. Each one also has a row in check codes.
| Code | Meaning |
|---|
Runtime errors from workflows, realtime and health
These runtime codes come from the specs workflows, realtime, health. Each one also has a row in check codes.
| Code | Meaning |
|---|
Runtime errors from search, feeds, moderation and media
These runtime codes come from the specs search, feeds, moderate and media, all planned for 0.4. Each one also has a row in check codes.
| Code | Meaning |
|---|---|
MAIN-SEARCH-0001 | The index isn't declared under search.indexes (400) |
MAIN-SEARCH-0002 | The access filter can't be expressed in the engine's query; refused, never post-filtered (403) |
MAIN-SEARCH-0003 | search.query without for, outside tenant.system (400) |
MAIN-SEARCH-0004 | The engine is unreachable or the query passed timeout (503, retryable) |
MAIN-SEARCH-0005 | A filter, sort or facet names an undeclared or reserved field (400) |
MAIN-SEARCH-0006 | No index endpoint for the tenant's region; the change stays queued |
MAIN-SEARCH-0007 | A cursor would go past max-depth (400) |
MAIN-SEARCH-0008 | q is too long, or a filter value has the wrong type (400) |
MAIN-SEARCH-0009 | An index change is in the dead-letter queue after its last retry |
MAIN-FEED-0001 | Refused: one side blocks the other (403) |
MAIN-FEED-0002 | A follow or item across tenants (403) |
MAIN-FEED-0003 | The item doesn't exist, is removed, or isn't visible to the viewer (404) |
MAIN-FEED-0004 | Edit or remove by someone who isn't the author or allowed feeds.remove (403) |
MAIN-FEED-0005 | Following more than max-following (422) |
MAIN-FEED-0006 | A fan-out batch is in the dead-letter queue; timelines merge at read time meanwhile |
MAIN-FEED-0007 | The ranker failed or was too slow; the chronological timeline was served |
MAIN-FEED-0008 | A pushed value loosens a feed limit; refused, last good kept |
MAIN-MOD-0001 | Report refused by the rate limit or as a repeat (429) |
MAIN-MOD-0002 | Unknown reason, or the target isn't in the current tenant (400) |
MAIN-MOD-0003 | The caller lacks moderate.review, or the case is someone else's (403) |
MAIN-MOD-0004 | Appeal refused: outside the window, a repeat, or not the caller's (409) |
MAIN-MOD-0005 | An appeal resolved by the reviewer who decided (403) |
MAIN-MOD-0006 | A pre-screen driver failed; the content stays held |
MAIN-MOD-0007 | Known-abuse hash match; the content is blocked and never published |
MAIN-MOD-0008 | A decision without a ground or facts (400) |
MAIN-MOD-0009 | Held, blocked or removed content was read (403) |
MAIN-MEDIA-0001 | The source file isn't ready, isn't in this tenant, or isn't readable by the caller (404) |
MAIN-MEDIA-0002 | The source is over a duration, size or pixel limit (422) |
MAIN-MEDIA-0003 | The source's type isn't supported or doesn't match its declared type (415) |
MAIN-MEDIA-0004 | The driver doesn't support this kind or format (501) |
MAIN-MEDIA-0005 | A transform step failed after its last retry; the job is failed |
MAIN-MEDIA-0006 | No job with that id in the current tenant (404) |
MAIN-MEDIA-0007 | media.keep lists location or an unknown metadata name; start fails |
MAIN-MEDIA-0010 | The ffmpeg driver in production; logged once at start |
Runtime errors from protect
These runtime codes come from protected files (spec protect-taps, planned for 0.4). A denial carries the TAPS error type in a taps member and never names the failed condition. Each one also has a row in check codes.
| Code | Meaning |
|---|---|
MAIN-PROTECT-0001 | Licence denied by the policy (403); the TAPS error type is in taps, the failed condition only in the decision log |
MAIN-PROTECT-0002 | Time-locked: before releaseAt, or the release round isn't available yet (403, retryAfter) |
MAIN-PROTECT-0003 | The asset, the person's access, the link or the licence was revoked, or the asset was erased (403; 410 when erased) |
MAIN-PROTECT-0004 | No such asset, version, link, delegation or approval in this tenant (404, same body either way) |
MAIN-PROTECT-0005 | A person must approve first; the problem carries approvalId and retryAfter (403) |
MAIN-PROTECT-0006 | The approver declined, or the approval timed out (403) |
MAIN-PROTECT-0007 | Delegation invalid: wider than the person, expired, revoked, used up or deeper than 3 (403) |
MAIN-PROTECT-0008 | A query or play budget, or a delegation's maxUses, is used up (429) |
MAIN-PROTECT-0009 | A session, print, device or recipient-verification rate limit was passed (429, retryAfter) |
MAIN-PROTECT-0010 | Suite not supported: TAPS-CS3 is refused until it is registered; no post-quantum protection is claimed (400) |
MAIN-PROTECT-0011 | Threshold release not reached: fewer than t parties have released (403, retryable) |
MAIN-PROTECT-0012 | The asset or its label is locked down, suspended or quarantined (423) |
MAIN-PROTECT-0013 | Step-up required: the policy needs a fresher or stronger sign-in, listed in requirements (401) |
MAIN-PROTECT-0014 | The TAPS Node, a threshold party or the sealing service is unreachable (503, retryable); nothing opens without a licence |
MAIN-PROTECT-0015 | Invalid input: unknown policy key, label pan, a zone id that isn't opaque, t outside 2 to n, releaseAt in the past, an unknown party or an undeclared agent (400) |
MAIN-PROTECT-0016 | The file's version is superseded; location names the current version (409) |
MAIN-PROTECT-0017 | The ledger is stale or the registration is pending (503, retryAfter) |
MAIN-PROTECT-0018 | A query answer was refused by the output controls: the whole document, a verbatim span over the limit, or a denied field (422) |
MAIN-PROTECT-0019 | The driver's profile lacks the capability the call needs, such as query or web3 (501) |
Runtime errors from licence
These runtime codes come from app licensing (spec licence, built; codes from 0020 come from MainSDK Cloud, spec licence-cloud, planned for 0.6). A refusal never includes the licence's claims, its id or the machine id. Each one also has a row in check codes.
| Code | Meaning |
|---|---|
MAIN-LICENCE-0001 | The feature needs a licence and the licence in scope doesn't include it, or isn't valid or in grace (403, with licence: { feature, status, reason }) |
MAIN-LICENCE-0002 | A licensed limit is reached: the licence's ceiling is the one that binds (403, with licence: { name, limit }) |
MAIN-LICENCE-0003 | A licensed limit was passed with over: report; the call was allowed. Logged once per limit and scope per UTC day |
MAIN-LICENCE-0004 | A licence, CA certificate or revocation list doesn't verify; reason is signature, chain, format, subject, binding, rollback or test-root (400 from licence.update()) |
MAIN-LICENCE-0005 | The licence is in grace: expiring, revoked or suspended. Warned once a day with graceEndsAt |
MAIN-LICENCE-0006 | The licence is expired, revoked or suspended past its grace; licensed features are off |
MAIN-LICENCE-0007 | No licence, or not yet valid; licensed features are off |
MAIN-LICENCE-0008 | Refresh failed, or the newest revocation list is past its nextUpdate; the current state is kept. Logged once per outage |
MAIN-LICENCE-0009 | An older licence (seq) or revocation list (number), or a different list with the same number, was refused; the newer one stays |
MAIN-LICENCE-0010 | The clock is behind the high-water time by more than licence.skew; the high-water time is used for licence decisions |
MAIN-LICENCE-0013 | A feature name that isn't under licence.features: has returns false and logs once; require throws (500) |
MAIN-LICENCE-0015 | The machine id can't be read: no /etc/machine-id for machine: os, or MAINSDK_MACHINE_ID unset for machine: env (500) |
MAIN-LICENCE-0016 | licence.update refused: no licence.manage permission, an app licence outside tenant.system() in a tenanted app, or a tenant licence for another tenant (403) |
MAIN-LICENCE-0020 | Cloud: the caller lacks the role, or a write has no passkey session from the last 5 minutes (403) |
MAIN-LICENCE-0021 | Cloud: the licence claims are invalid; errors names each field (422) |
MAIN-LICENCE-0022 | Cloud: no such root, CA or licence in this account (404) |
MAIN-LICENCE-0023 | Cloud: the change conflicts with the state, such as renewing a revoked licence or issuing from a retired CA (409) |
MAIN-LICENCE-0024 | Cloud: a second approver is needed; the problem carries approvalId |
MAIN-LICENCE-0025 | Cloud: more than 60 refresh calls for one licence in an hour (429, Retry-After) |
MAIN-LICENCE-0026 | Cloud: the key service can't sign; nothing was issued or changed (503, retryable) |
Runtime errors from share and links
These runtime codes come from share (spec files-share, ADR-0056) and links (spec links, planned for 0.4), and from the sign-in link method of auth. The check findings MAIN-SHARE-0001, MAIN-SHARE-0007 and MAIN-LINK-0003 to MAIN-LINK-0005 are in check codes.
| Code | Meaning |
|---|---|
MAIN-SHARE-0008 | share.driver: local in production; the app refuses to start |
MAIN-SHARE-0009 | A local share link was opened after it expired or was revoked (410) |
MAIN-SHARE-0010 | share.update was asked to keep a share forever (400) |
MAIN-SHARE-0011 | share.placements.create failed its row checks and created nothing (422, rows listed) |
MAIN-SHARE-0012 | A getqr.id webhook event type outside the pinned contract; acknowledged and logged once |
MAIN-SHARE-0013 | Personal data in a share's title or metadata, too many or reserved metadata keys, or someone else's contact card (400) |
MAIN-LINK-0001 | Invite link used up, expired, revoked, or for another email (410) |
MAIN-LINK-0002 | A screen or params not declared under links.routes (400) |
MAIN-LINK-0005 | links.signIn without link in auth.methods (400) |
MAIN-AUTH-0046 | Sign-in link used, expired, unknown, or opened in another browser (401) |
Runtime errors from the modern AI specs
These runtime codes come from multimodal input, images, video, the agent loop, routing and local models (specs ai-multimodal, ai-image, ai-video, ai-run, ai-routing, ai-local), documents (documents), sandboxes (sandbox), languages (i18n) and voice agents (ai-voice). Their check findings, and the EVAL codes the CLI raises, are in check codes.
| Code | Meaning |
|---|---|
MAIN-AI-0040 | A prompt part is invalid: unknown type, no file, a mime that doesn't match the bytes, pages out of order, or video given as bytes (400); nothing is sent |
MAIN-AI-0041 | A prompt part is over a limit: size, pixels, pages, duration, frames or max-parts (413); nothing is sent |
MAIN-AI-0042 | The model or driver doesn't accept this kind of part (501) |
MAIN-AI-0043 | A labelled image, audio or scan would go to a driver whose data use or region doesn't meet the rules, and media can't be redacted (403) |
MAIN-AI-0045 | An image option is invalid: size, count, mask size or an empty prompt (400); nothing is sent |
MAIN-AI-0046 | The image driver doesn't offer this operation (501) |
MAIN-AI-0047 | The provider refused the image prompt under its content policy (422) |
MAIN-AI-0048 | A content credential couldn't be signed and ai.credentials.required is true (500) |
MAIN-AI-0050 | A video option is invalid: seconds, size, prompt or references (400); nothing is sent |
MAIN-AI-0051 | No video job with that id in the current tenant (404) |
MAIN-AI-0052 | The provider failed or refused the video job; the job is failed |
MAIN-AI-0053 | A video job lost its provider heartbeat and was cancelled at the provider |
MAIN-AI-0060 | An ai.run option is invalid: goal and messages, maxSteps, budget, timeout or agent (400); nothing is sent |
MAIN-AI-0061 | The run stopped at maxSteps (stop: "max-steps") |
MAIN-AI-0062 | The run stopped at its budget (stop: "budget") |
MAIN-AI-0063 | The run stopped at its timeout (stop: "timeout") |
MAIN-AI-0064 | The model asked for a tool that isn't in the run's tools; the step was refused |
MAIN-AI-0065 | A handoff or delegation names an agent not declared for it (400) |
MAIN-AI-0066 | A delegation asked for more than the parent's remaining budget or the declared share (402) |
MAIN-AI-0067 | No run with that id in the current tenant, or it isn't waiting (404 or 409) |
MAIN-AI-0071 | Every model in the route failed with a retryable error (502) |
MAIN-AI-0072 | A fallback model was skipped because its endpoint is outside the tenant's region for labelled data (logged) |
MAIN-AI-0073 | A response cache entry is invalid: tenant scope with labelled data, a TTL over 7 days, or local kv in production |
MAIN-AI-0074 | A batch option is invalid: no items, too many, duplicate ids, or no batch API (400) |
MAIN-AI-0075 | No batch with that id in the current tenant (404) |
MAIN-AI-0078 | The local model server is unreachable (503, retryable) |
MAIN-AI-0080 | The model isn't on the local server (404) |
MAIN-DOC-0001 | The file isn't a supported document type, or its bytes don't match its declared type (415) |
MAIN-DOC-0002 | Over a limit: size, pages, ZIP expansion, or more pages than inline-pages for parse (413) |
MAIN-DOC-0003 | Pages have no text layer and OCR is off or no OCR driver is set (422) |
MAIN-DOC-0004 | The documents driver doesn't offer this (501) |
MAIN-DOC-0005 | The PDF is encrypted or password protected (422) |
MAIN-DOC-0006 | Active content was found and not run (logged) |
MAIN-DOC-0007 | A render layout or template is invalid (400) |
MAIN-DOC-0008 | A labelled document would go to a driver outside the tenant's region or not meeting the data rules (403) |
MAIN-DOC-0009 | No document job with that id in the current tenant (404) |
MAIN-SANDBOX-0001 | A sandbox option is invalid (400); nothing ran |
MAIN-SANDBOX-0002 | The code reached its time limit and was killed |
MAIN-SANDBOX-0003 | The code passed its memory limit and was killed |
MAIN-SANDBOX-0004 | Output or files passed their limit; output is cut |
MAIN-SANDBOX-0005 | A host isn't in sandbox.network and network.allow, or the code tried a host it wasn't given (403) |
MAIN-SANDBOX-0006 | The sandbox driver is unavailable (503, retryable) |
MAIN-SANDBOX-0007 | The run would pass, or reached, its cost ceiling (402) |
MAIN-SANDBOX-0008 | No sandbox session with that id in the current tenant, or it was closed (404) |
MAIN-SANDBOX-0009 | A secret reference isn't under sandbox.secrets (403) |
MAIN-SANDBOX-0011 | The local sandbox driver in production |
MAIN-I18N-0004 | A message was missing and a fallback was used (logged once per key and locale) |
MAIN-I18N-0005 | t() was called without a value its message needs (logged) |
MAIN-I18N-0007 | An agent or service tried to approve a translation draft (403) |
MAIN-I18N-0009 | No message for the key in any locale; production never shows the key (logged) |
MAIN-VOICE-0001 | A voice session option is invalid (400) |
MAIN-VOICE-0002 | The voice driver doesn't offer this (501) |
MAIN-VOICE-0003 | Recording refused: recording off, or no consent from the caller (403) |
MAIN-VOICE-0004 | The session reached its duration or cost limit and ended after a closing line |
MAIN-VOICE-0005 | A telephony or room webhook failed signature verification (401) |
Runtime errors from db and kv
These runtime codes come from db (specs db, db-writes and db-queries) and kv (spec kv). The check findings MAIN-DATA-0013, MAIN-DATA-0020, MAIN-DATA-0021, MAIN-DATA-0030, MAIN-DATA-0040 to MAIN-DATA-0045, MAIN-DATA-0051, MAIN-DATA-0055, MAIN-DATA-0056, MAIN-DATA-0061, MAIN-DATA-0062 and MAIN-DATA-0065 are in check codes.
| Code | Meaning |
|---|---|
MAIN-DATA-0008 | The record isn't there, with tenancy off (404) |
MAIN-DATA-0009 | A create repeats an id or a declared unique value (409) |
MAIN-DATA-0031 | The local SQLite driver can't start: this Node has no node:sqlite (500) |
MAIN-DATA-0032 | db.driver is not built, or a Postgres driver has no client (400) |
MAIN-DATA-0050 | A query ran past its statement timeout and was cancelled (503, not retried) |
MAIN-DATA-0052 | No connection was free within db.pool.acquire (503 with Retry-After: 1) |
MAIN-DATA-0053 | A query took longer than db.slow (warn log, once a minute per query shape) |
MAIN-DATA-0054 | One query shape ran more than db.repeat times in one request or job (warn log) |
MAIN-DATA-0060 | ifVersion doesn't match the record's version (409; 412 from If-Match) |
MAIN-DATA-0063 | restore of a record not in trash, or past its trash period (404) |
MAIN-DATA-0066 | A write to a versioned record came with no If-Match (428) |
MAIN-KV-0001 | incr was given a step that is not a whole number (400), or the key holds a value that is not a whole number (409) |
MAIN-KV-0002 | ttl is not a positive duration (400) |
MAIN-KV-0003 | A key is empty or longer than 512 characters (400) |
Codes from other specs
These runtime codes are defined in a spec's Codes table and listed here automatically. The spec holds the full cause, and npx mainsdk explain <code> gives the fix.
| Code | Raised by | Meaning | Spec |
|---|---|---|---|
MAIN-ACCESS-0030 | runtime | access.relate got a relation with no name, or a resource with no id (400). Fix: call access.relate(team, "editor", { type: "folder", id: "f1" }) | access |
MAIN-ACCESS-0031 | runtime | access.list was asked for an action that has a named rule; a rule cannot be pushed into a database query, so the list is refused instead of widened (400). Fix: list through db and check each row with access.can, or drop the rule from this action | access |
MAIN-ADVISOR-0002 | runtime | The model's output didn't fit: a choice outside may, evidence not in the input, a reason over 600 characters, or not the schema; refused and logged | advisor |
MAIN-ADVISOR-0003 | runtime | The advisor stopped: budget spent, max-per-hour reached, or the model failed; playbooks carry on | advisor |
MAIN-ADVISOR-0004 | runtime | The input matched a secret or personal-data detector after redaction; the field was dropped before the call | advisor |
MAIN-AI-0069 | runtime | The model's driver can't call tools, so a run with tools can't start (501); nothing is sent | ai-run |
MAIN-ANALYTICS-0009 | runtime | The analytics section of main.yaml is invalid: an event or metric declaration that cannot be read, a bad to, without-consent or min-group (500) | analytics |
MAIN-APIKEY-0012 | runtime | A manage call (rotate, revoke, get) names a key that isn't in the current tenant (404, same body for another tenant's). Fix: list the tenant's keys to find the id | apikeys |
MAIN-APP-0001 | runtime | The API service reference app could not sync a repository with the partner (502). Fix: Look at the dead-letter queue with jobs.dead.list() and retry the job once the partner is back | reference-architectures |
MAIN-APP-0002 | runtime | The API service reference app's partner answered that it is not ready (503, retryable). Fix: Retry; the job backs off and lands in the dead-letter queue after three attempts | reference-architectures |
MAIN-APP-0003 | runtime | The payments checkout reference app refused an order amount that is not a positive whole number of minor units (400), or the payment provider answered without a checkout session (502). Fix: Send a positive integer in minor units; on a provider answer, retry the checkout once the provider is back | reference-architectures |
MAIN-APPS-0010 | runtime | A record would hold a field outside mainsdk-app@1; refused before anything is written | app-registry |
MAIN-APPS-0011 | runtime | A declared peer app is not reachable on the link: no running record, no socket, or it did not answer | app-registry |
MAIN-AUTH-0002 | runtime | The person is signed in but may not do this: no role the action needs, not a manage role for people, teams and invites, a non-owner touching the owner role or requiring SSO, or an SSO user nobody added while just-in-time joining is off (403). Fix: ask an owner to grant the role or to add the person | auth |
MAIN-AUTH-0003 | runtime | The session's assurance is too low: the action needs a stronger sign-in, or its admin role needs a phishing-resistant one (403). Fix: sign in with a passkey, or through SSO with phishing-resistant MFA | auth |
MAIN-AUTH-0004 | runtime | Sign-in failed (401). One body for every bad credential, password, code or passkey, and for a passkey already claimed by another account; the reason is only in the log. Fix: check the details and try again, or use another method | auth |
MAIN-AUTH-0005 | runtime | A sign-in method that auth.methods doesn't list was used, or domain auto-join was used while it is off (403). Fix: add the method to auth.methods, or set auth.orgs.domains.auto-join: true | auth |
MAIN-AUTH-0010 | runtime | The action needs a recent sign-in (a fresh window, or the last 10 minutes for sensitive changes) and the session's is older (401). Fix: ask the person to sign in again, then retry | auth |
MAIN-AUTH-0011 | runtime | auth.key is not set. In production the app refuses to sign sessions (500); elsewhere it uses a random key and logs a warning, so sessions end when the process does. Fix: set auth.key: secret:AUTH_KEY to a random 32-byte value | auth |
MAIN-AUTH-0013 | runtime | The WhatsApp sender couldn't deliver the code (502, retryable on an outage or timeout). The error holds neither the code, the number nor the provider's reply. Fix: check the sender settings and delivery log at the provider | auth |
MAIN-AUTH-0014 | runtime | A phone number isn't valid E.164 (400). Fix: use the international format with a leading +, such as +919876543210 | auth |
MAIN-AUTH-0015 | runtime | Too many sign-in or code attempts from one source or for one account (429, with Retry-After). Fix: wait, then try again | auth |
MAIN-AUTH-0016 | runtime | The refresh token is missing, unknown, expired or already used (401). Fix: sign in again | auth |
MAIN-AUTH-0017 | runtime | Sign-up is closed (403), or an account with this email or phone number already exists (409). Fix: ask the app owner for an invite, or sign in instead | auth |
MAIN-AUTH-0018 | runtime | An auth call got input it can't use (400): a bad email, a password outside the length range, a role not under tenancy.roles, a team name, a domain, expiresIn, a device scope, a SCIM group, or an SSO setting. Fix: the problem names the value; correct it | auth |
MAIN-AUTH-0019 | runtime | Account recovery was tried before its waiting period ended (403, retryable, with Retry-After). Fix: try again after the wait; a signed-in device can cancel the recovery until then | auth |
MAIN-AUTH-0020 | runtime | The sign-in state couldn't be read or locked, so the request was refused rather than guessed (503, retryable); a warning also says production runs sessions on the local kv driver, which instances don't share. Fix: retry; check the kv and db drivers | auth |
MAIN-AUTH-0021 | runtime | A security event sink or an onRisk hook threw. It is logged and sign-in goes on. Fix: repair the sink or hook | auth |
MAIN-AUTH-0022 | runtime | The breached-password check timed out or failed; the password was assessed without it and this was logged. Fix: check that breached.driver is reachable | auth |
MAIN-AUTH-0023 | runtime | whatsapp sign-in or a phone code was used with no sender configured (500). Fix: set auth.whatsapp.driver in main.yaml, or pass whatsapp to createApp | auth |
MAIN-AUTH-0024 | runtime | Passkeys or the device flow need the origins the app is served from and auth.passkeys.origins is empty (500). Fix: set auth.passkeys.origins, such as [https://app.example.com] | auth |
MAIN-AUTH-0025 | runtime | No email driver is set: recovery by email fails (500) and an invite link is not sent (logged); or the invite's getqr.id link couldn't be revoked (logged, the invite is revoked anyway). Fix: pass an email driver to createApp | auth |
MAIN-AUTH-0026 | runtime | The DNS lookup for a domain proof failed; the proof counts as not found and this is logged. Fix: check DNS, then run verify again | auth |
MAIN-AUTH-0027 | runtime | A device code is unknown, used or expired (404). Fix: start again on the device to get a new code | auth |
MAIN-AUTH-0050 | runtime | Step-up required before the session is issued (401; the problem lists the methods and carries the step-up challenge) | auth |
MAIN-AUTH-0051 | runtime | Sign-in refused: the source network is blocked (403) | auth |
MAIN-AUTH-0052 | runtime | Step-up challenge wrong, expired, reused or for another sign-in; the pending sign-in is closed (401) | auth |
MAIN-AUTH-0054 | runtime | The new password is in a known breach (422) | auth |
MAIN-AUTH-0056 | runtime | The identity provider did not answer or answered with a server error or redirect (502); the message carries neither the request nor the reply | auth |
MAIN-AUTH-0060 | runtime | No such member, team, invite, passkey or device in the current tenant (404, same body for another tenant's). Fix: check the id in the current organisation | auth |
MAIN-AUTH-0061 | runtime | A team with that name already exists in the organisation (409). Fix: pick another name | auth |
MAIN-AUTH-0080 | runtime | An auth route got the wrong method, a body that isn't JSON, or a field that fails its schema (400 or 405) | auth-client |
MAIN-AUTH-0084 | runtime | A sign-in route was called for a method auth.methods doesn't list (404, so the list of methods isn't probed one by one) | auth-client |
MAIN-AUTH-0100 | runtime | A request to an action marked attest: required carried no valid App Attest or Play Integrity token, or its nonce was unknown, used or older than 60 seconds; the refusal is audited | expo-adapter |
MAIN-AUTH-0102 | runtime | A QR sign-in code was malformed, unknown, expired, already used or approved by someone who is not signed in; the reason is in the error extensions | mobile-auth |
MAIN-AUTH-0103 | runtime | A push approval was refused: too many prompts in the window (429), or no registered device could be reached (503) | mobile-auth |
MAIN-AUTH-0104 | runtime | Account recovery could not start (no valid proof) or is not ready (waiting period not over, or cancelled) | mobile-auth |
MAIN-AUTH-0105 | runtime | A trusted device was revoked, expired or never enrolled, so its session no longer authorises anything | mobile-auth |
MAIN-AUTH-0106 | runtime | A native passkey assertion named a credential that is not registered for the person | mobile-auth |
MAIN-AUTH-0200 | runtime | The approval covers a different action, amount, payee or person than the one about to run (403); the approval is voided. Fix: ask the person to approve the exact action again | signed-approvals |
MAIN-AUTH-0201 | runtime | The approval is older than its expiry, five minutes by default (403). Fix: ask the person to approve again | signed-approvals |
MAIN-AUTH-0202 | runtime | The approval was already used, never signed or never asked for (403). Fix: ask the person to approve again | signed-approvals |
MAIN-AUTH-0203 | runtime | The passkey signature does not cover this approval, the passkey is not the person's, or the person has none (401). Fix: sign again on the approval screen with the person's own passkey | signed-approvals |
MAIN-AUTH-0204 | runtime | The action needs a signed approval and none came, or the person cancelled (428). The MCP answer carries an elicitation naming what to approve. Fix: ask the person to approve, then call again with the approval id | signed-approvals |
MAIN-AUTH-0205 | runtime | A critical action needs a second, different signer and the same person signed twice (403). Fix: ask another operator to approve | signed-approvals |
MAIN-AUTH-0206 | runtime | The approval request has no action name or a bad amount or currency (400). Fix: pass auth.confirm("payouts.send", { amount: { value, currency }, payee }) | signed-approvals |
MAIN-BACKUP-0004 | runtime | A drill was asked to restore somewhere other than scratch, or the driver landed it in production (400). Fix: run with --to scratch and make the driver restore into a new branch or database | backup-restore-evidence |
MAIN-BACKUP-0005 | runtime | The provider did not return backup settings (502, retryable). Fix: give the app a read-only provider token and retry | backup-restore-evidence |
MAIN-BOT-0001 | runtime | Challenge required (403; the problem's challenge member carries a BotChallenge) | bot-defence |
MAIN-BOT-0002 | runtime | Refused as automated (403; the reasons are logged, not returned) | bot-defence |
MAIN-BOT-0003 | runtime | Challenge solution wrong, expired, for another challenge, or used twice (403) | bot-defence |
MAIN-BOT-0004 | runtime | Form token missing, forged or older than 1 hour (403) | bot-defence |
MAIN-BOT-0006 | runtime | The CAPTCHA provider didn't answer within 2 s; the built-in challenge was used instead (logged once per outage) | bot-defence |
MAIN-BOT-0007 | runtime | A WhatsApp or SMS code was refused before sending: country not allowed, or pumping velocity reached (429) | bot-defence |
MAIN-CACHE-0001 | runtime | A credentialed response carried a shared-cache directive; it was replaced with private, no-store (logged once per route and process) | http-cache |
MAIN-CACHE-0002 | runtime | An action declared cache.scope: public read a session, an API key or the tenant; the response went out private, no-store and the error is logged | http-cache |
MAIN-CACHE-0003 | runtime | A cache key is longer than 256 characters, or a value isn't JSON or is larger than cache.max-value (400); nothing is stored | http-cache |
MAIN-CACHE-0004 | runtime | An app-scoped value matched a personal-data detector; it was not stored and the function's result was returned | http-cache |
MAIN-CACHE-0005 | runtime | The cache store was slower than cache.timeout or failed; reads and writes are skipped until it answers (logged once per outage) | http-cache |
MAIN-CANARY-0001 | runtime | A canary record was returned by a query (critical security event; the response is not changed unless on-touch: block) | exfiltration-guard |
MAIN-CANARY-0002 | runtime | The canary API key or canary secret was used, sent or received (critical security event) | exfiltration-guard |
MAIN-CLOUD-0001 | runtime | Cloud: the project or account is pinned to another region; the call is refused and nothing is stored here (421). The problem carries region and location | cloud-platform |
MAIN-CLOUD-0002 | runtime | Cloud: the mainsdk-protocol header is missing or names a wire version the Cloud does not read (400); the problem names the header | cloud-platform |
MAIN-CLOUD-0003 | runtime | Cloud: the SDK major in mainsdk-sdk-major is not among the current and two previous majors (426) | cloud-platform |
MAIN-CLOUD-0004 | runtime | Cloud: no ingest key or CI identity token, or one that is not valid for this project (401). A project ID is never a credential | cloud-platform |
MAIN-CLOUD-0005 | runtime | Cloud: no signed-in session, or it expired (401) | cloud-platform |
MAIN-CLOUD-0006 | runtime | Cloud: the request is not valid: a project id that is not a UUIDv7, an unknown region or environment, a bad batch or an unknown device code (422, or 404 for a code) | cloud-platform |
MAIN-CLOUD-0007 | runtime | Cloud: there is no such add-on (404) | usage-billing |
MAIN-CLOUD-0008 | runtime | Cloud: no such project or account in this account; the same answer whether it exists elsewhere or not (404) | cloud-platform |
MAIN-CLOUD-0009 | runtime | Cloud: the project id is claimed under another git remote or by another account; the CLI offers a new id (409, claimedRemote) | cloud-platform |
MAIN-CLOUD-0010 | runtime | Cloud: production needs a passkey or SSO with phishing-resistant MFA; a one-time code or social sign-in does not count (403, required) | production-access |
MAIN-CLOUD-0011 | runtime | Cloud: this action needs a fresh passkey assertion bound to it; the problem carries stepUp with the challenge and the prompt (403) | production-access |
MAIN-CLOUD-0012 | runtime | Cloud: sharing through getqr.id is not configured for this Cloud (503) | cloud-platform |
MAIN-CLOUD-0403 | runtime | Cloud: Free has one seat; a second member needs Teams (402, upgrade). A read-only getqr.id link needs no seat | usage-billing |
MAIN-CLOUD-0500 | runtime | Cloud: the Cloud could not finish the call (500). The app is never affected | cloud-platform |
MAIN-CONN-0010 | runtime | connect got a returnTo that isn't a path in this app: it must start with a single / (400) | connections |
MAIN-CONNECTOR-0002 | runtime | A call to the tool failed or was refused with a 4xx or 5xx; the audit log line names the operation and status (502) | connectors |
MAIN-CONTEXT-0002 | runtime | A share or receive was held: no recorded consent for the purpose on this side, or the peer reported none; nothing was sent or stored | context |
MAIN-CONTEXT-0003 | runtime | A peer isn't first-party: its seal's owner differs from this app's, or it doesn't declare the same group; refused | context |
MAIN-CONTEXT-0004 | runtime | A field or value was refused: not in the purpose's list, a string over 64 characters, a labelled personal, sensitive, child or phi field, or a value matching a secret, email, phone or card detector | context |
MAIN-CONTEXT-0005 | runtime | A cross-tenant flow was refused: the receiving context id isn't in the sender's tenant | context |
MAIN-CONTEXT-0007 | runtime | An erasure couldn't reach a peer; it was queued and retried, and the receipt lists the peer as pending | context |
MAIN-CRYPTO-0001 | runtime | A crypto input is the wrong shape (400): a key that isn't 32 bytes, an HMAC key under 16 bytes, a token under 16 bytes, random bytes outside 1 to 65536, bad base64url, or an ed25519 key that doesn't import. Fix: pass the size or format the problem names, for example crypto.generateKey() | keys |
MAIN-CRYPTO-0003 | runtime | A sealed box couldn't be opened (400): too short, a wrong key, changed bytes or a different aad. Fix: pass the whole value crypto.seal returned, with the key and aad that sealed it | keys |
MAIN-CRYPTO-0010 | runtime | The platform has no implementation of the requested post-quantum algorithm (for example SLH-DSA, or ML-KEM on Bun); nothing is substituted silently | post-quantum |
MAIN-CRYPTO-0011 | runtime | FIPS mode is on and the call asked for an algorithm that is not approved | post-quantum |
MAIN-CRYPTO-0012 | runtime | FIPS mode is on and this platform has no FIPS-validated crypto provider | post-quantum |
MAIN-DATA-0064 | runtime | The outbox couldn't relay an entry after its last retry; the entry moved to the job dead-letter queue | db-writes |
MAIN-DEPLOY-0006 | runtime | A tick arrived without the bearer secret, or with the wrong one (401); nothing ran | deploy |
MAIN-DEPLOY-0007 | runtime | A tick named a schedule the app doesn't declare (404) | deploy |
MAIN-DEPLOY-0008 | runtime | A tick arrived later than deploy.tick.late after its slot; it ran, and the delay is logged | deploy |
MAIN-DEVICE-0005 | runtime | An envelope was refused: payload not in JCS form, unknown payload type, bad signature, unknown key id, aud not this app (401) | device-identity |
MAIN-DEVICE-0006 | runtime | A request was not fresh: iat more than 10 minutes from server time, or the nonce was seen in the last 20 minutes (401, with serverTime) | device-identity |
MAIN-DEVICE-0007 | runtime | An enrolment proof was refused: the token is unknown, expired, used up or revoked; the code wasn't approved or expired; the OIDC token doesn't match a declared issuer, audience and subject (403). Evidence below a required level is MAIN-DEVICE-0023 | device-identity |
MAIN-DEVICE-0008 | runtime | The device is revoked; every request from it is refused (403, with a signed revoked object) | device-identity |
MAIN-DEVICE-0009 | runtime | The credential expired more than renew-grace ago; the device must enrol again (401) | device-identity |
MAIN-DEVICE-0010 | runtime | A rotation was refused: a signature from the old or new key is missing, prev doesn't match the current credential, or the new key was used before (409) | device-identity |
MAIN-DEVICE-0011 | runtime | The tenant has device.max devices that aren't revoked; enrolment refused (403) | device-identity |
MAIN-DEVICE-0012 | runtime | The device API is busy or its store doesn't answer; try later (503, Retry-After) | device-identity |
MAIN-DEVICE-0013 | runtime | The device belongs to another region; the response names the base to use (421) | device-identity |
MAIN-DEVICE-0014 | runtime | A device.* call was refused: no tenant in context, or no device.manage or device.view permission (403) | device-identity |
MAIN-DEVICE-0015 | runtime | A client call was refused: enrol on an enrolled client without first deleting its key, or a key store that can't be read or written | device-identity |
MAIN-DEVICE-0017 | runtime | Too many enrolment attempts for one token or one address; try later (429, Retry-After) | device-identity |
MAIN-DEVICE-0018 | runtime | The device or enrolment doesn't exist in the caller's tenant; a device of another tenant is reported the same way (404) | device-identity |
MAIN-DEVICE-0020 | runtime | Evidence was refused: format not accepted, binding or nonce wrong or older than 60 seconds, a chain to a root the tenant doesn't accept, failed boot verification, a security level under min-security-level, or a measurement not in builds. The device gets the code only; the server log names the check (403) | device-attest |
MAIN-DEVICE-0021 | runtime | A device missed its re-attestation past grace; its level dropped to none (logged, device.attestation.failed signal) | device-attest |
MAIN-DEVICE-0023 | runtime | An enrolment, a renewal, a policy target or a command needs a higher attestation level than the device holds (403, with need and have) | device-attest |
MAIN-DEVICE-0024 | runtime | The device's TCB is out-of-date past its grace or revoked; renewal refused until it re-attests on a fixed platform (403) | device-attest |
MAIN-DEVICE-0030 | runtime | client.send refused a record: the stream isn't declared in the client or by the server, the record isn't a JSON object, it is over max-record, or it fails the stream's schema on the device | device-telemetry |
MAIN-DEVICE-0031 | runtime | A batch is over max-batch or holds more than 1,000 records (413) | device-telemetry |
MAIN-DEVICE-0032 | runtime | A batch is out of order: seq isn't the next one, or prev doesn't match the last accepted batch (409, with expect) | device-telemetry |
MAIN-DEVICE-0033 | runtime | A record was rejected: unknown stream, over its stream's max-record, or it fails the stream's schema on the server | device-telemetry |
MAIN-DEVICE-0034 | runtime | A record was rejected because the detectors found a secret, or personal data in a stream with personal: false | device-telemetry |
MAIN-DEVICE-0035 | runtime | Telemetry is rate limited: the device's rate or the tenant's daily bytes is reached (429, Retry-After) | device-telemetry |
MAIN-DEVICE-0036 | runtime | A handler threw or took longer than 10 seconds; the batch wasn't acknowledged and will come again (503) | device-telemetry |
MAIN-DEVICE-0037 | runtime | The client's buffer is full or holds records older than queue.age; the oldest were dropped (logged once an hour with counts per stream) | device-telemetry |
MAIN-DEVICE-0045 | runtime | A publish was refused: no device.policy.define, content that fails the schema, an unknown group, or minClient that isn't a version (400) | device-policy |
MAIN-DEVICE-0046 | runtime | An overlay was refused: its path isn't under tighten, or the value wouldn't tighten the value in force (400) | device-policy |
MAIN-DEVICE-0047 | runtime | The device refused a bundle: bad signature, wrong aud or tenant, a version not above the one it holds, an overlay that loosens, or a minClient above its own; it kept its last good policy and reported why | device-policy |
MAIN-DEVICE-0048 | runtime | The device's apply handler threw; it kept its last good policy and reported failed with the error code | device-policy |
MAIN-DEVICE-0049 | runtime | A rollout stopped: the failed share passed rollback-if; the previous content was published again as a new version (logged, audited, device.policy.rolledback signal) | device-policy |
MAIN-DEVICE-0051 | runtime | The publish waits for approval; it applies when the people approve asks for have approved (202) | device-policy |
MAIN-DEVICE-0060 | runtime | A create was refused: the type isn't defined, the input fails its schema, the target is empty, or expiresIn is over max-ttl (400) | device-commands |
MAIN-DEVICE-0061 | runtime | The create targets more than max-targets devices without confirm equal to the count (409, with count) | device-commands |
MAIN-DEVICE-0062 | runtime | The command waits for approval (202); an app agent's approval was refused | device-commands |
MAIN-DEVICE-0063 | runtime | The device refused a command: bad signature, wrong aud, tenant or device, expired, an id it already ran, a type it hasn't registered, a risk that differs from its handler's, or fewer approvals than its floor; the receipt says refused | device-commands |
MAIN-DEVICE-0064 | runtime | The command expired before it was delivered or before a receipt came back | device-commands |
MAIN-DEVICE-0065 | runtime | The handler threw, timed out or returned a result that fails its schema; the receipt says failed | device-commands |
MAIN-DEVICE-0068 | runtime | A cancel or revert was refused: the command was already delivered, its type has no undo, or it isn't done (409) | device-commands |
MAIN-DEVICE-0069 | runtime | A receipt was refused: bad signature, an unknown command, or a digest that doesn't match the command sent (400) | device-commands |
MAIN-DEVICE-0070 | runtime | The device's command-log head differs from the one the server computes from its receipts (security event device.command_log_mismatch, high) | device-commands |
MAIN-DEVICE-0080 | runtime | A heartbeat was refused: its seq isn't above the last one (401) | device-health |
MAIN-DEVICE-0081 | runtime | The device's clock is more than 5 minutes off the app's; the offset is recorded and corrected for (logged once a day per device) | device-health |
MAIN-DEVICE-0083 | runtime | A device went silent (logged at info, device.silent signal) | device-health |
MAIN-DEVICE-0090 | runtime | The status list couldn't be refreshed; the last list is used until its validUntil, after which third parties treat credentials as unverifiable, while the app's own checks go on (logged once per outage) | device-taps |
MAIN-DEVICE-0091 | runtime | A delegation chain or approval decision on a command or publish is invalid: a broken chain, authority that widens down the chain, a revoked or expired link, or an approval by a key not in the tenant's DID document; the server refuses to send it and a device refuses to run it | device-taps |
MAIN-DEVICE-0093 | runtime | An agent passport was refused: an operator not listed, a passport not countersigned by the tenant, or a heartbeat reporting a model or version the passport doesn't name (TAPS-ATTEST §3: a change needs a new passport) | device-taps |
MAIN-DRIVER-0003 | runtime | Only verified developers can rate a driver (403). Fix: verify your developer record, then rate the driver | driver-marketplace |
MAIN-DRIVER-0004 | runtime | The developer has no project that runs the driver (403). Fix: rate a driver after your project runs it in production | driver-marketplace |
MAIN-DRIVER-0005 | runtime | A rating is not a whole number from 1 to 5 (400). Fix: send score as 1, 2, 3, 4 or 5 | driver-marketplace |
MAIN-E2E-0001 | runtime | e2e was used before configureE2e (500) | e2e |
MAIN-E2E-0002 | runtime | No such group on this device (404) | e2e |
MAIN-E2E-0003 | runtime | The request is invalid: a bad id, more than 1000 members, data over 4 MiB, a member with no key package, a member already in the group, or removing yourself (400, 413 for size) | e2e |
MAIN-E2E-0004 | runtime | The message can't be decrypted: this device holds no key for its epoch (a removed member, or a member added later), it was changed, or it belongs to another group (403, 400 for a value that is not a sealed message) | e2e |
MAIN-E2E-0005 | runtime | The passkey gave no PRF output (400) | e2e |
MAIN-E2E-0006 | runtime | Nothing to recover with this passkey: no recovery blob, or a different passkey (404) | e2e |
MAIN-E2E-0010 | runtime | The call asks for a suite or feature the driver does not implement (501) | e2e |
MAIN-E2E-0011 | runtime | The caller is not a member of the group, so it can't add, remove or encrypt (403) | e2e |
MAIN-E2E-0012 | runtime | A commit conflicted with another member's commit for the same epoch and the retries are used up (409) | e2e |
MAIN-EMAIL-0002 | runtime | The recipient is not a single address, or a header or the subject holds a line break (400) | email-notify |
MAIN-EMAIL-0003 | runtime | The template does not exist, or a placeholder has no value in data (400) | email-notify |
MAIN-EMAIL-0004 | runtime | The provider could not send the email; retryable says whether a retry or a fallback driver may help (502) | email-notify |
MAIN-EMAIL-0005 | runtime | The tenant's per-minute or per-day send limit is reached (429, retryAfter) | email-notify |
MAIN-EMAIL-0007 | runtime | The unsubscribe link was tampered with or is not valid (400) | email-notify |
MAIN-EMAIL-0008 | runtime | email.driver: local in production; the app refuses to start (500) | email-notify |
MAIN-EVENT-0001 | runtime | events.publish got a type that isn't lowercase words joined by dots, or data that isn't JSON (400); the outbox refuses a non-JSON event too. Fix: use a type such as order.shipped and plain JSON data | webhooks-jobs |
MAIN-EVENT-0014 | runtime | A handler or tap on events.on threw. It is logged and the other handlers still run. Fix: repair the handler; the event is not retried in process, so use jobs for work that must finish | webhooks-jobs |
MAIN-EXFIL-0001 | runtime | An outbound call carried a resolved secret to a host other than that secret's driver hosts (blocked in enforce) | exfiltration-guard |
MAIN-EXFIL-0002 | runtime | An outbound call carried labelled values read in this request to a host not declared as a receiver for that label (blocked in enforce) | exfiltration-guard |
MAIN-EXFIL-0003 | runtime | A response held: more records with labelled fields than the action's bulk allows for one response, or than actor-window allows for one actor over the window (403) | exfiltration-guard |
MAIN-EXFIL-0004 | runtime | A response carried a resolved secret or a card number; replaced with a 500 MAIN-CORE-0500 problem | exfiltration-guard |
MAIN-EXFIL-0005 | runtime | A body was larger than scan.max; the rest was not scanned (logged once per route a minute) | exfiltration-guard |
MAIN-FEED-0009 | runtime | A feeds argument or setting is invalid: a trending key outside ^[a-z0-9:_.-]{1,64}$, a bad until, order, visibility or limit (400) | feeds |
MAIN-FLEET-0001 | runtime | A fleet policy has an unknown key or is not plain data (400). Fix: send declarative JSON policy only, never code | fleet-control |
MAIN-FLEET-0002 | runtime | A policy or push is not signed by the tenant role with enough tenant keys; MainSDK's own key does not count (403). Fix: sign with the tenant's keys up to the role threshold | fleet-control |
MAIN-FLEET-0003 | runtime | A kill or response action is malformed, or lacks a second operator's approval and is not a logged break-glass (400 or 403). Fix: name the target and get a second signature, or use break-glass with a reason | fleet-control |
MAIN-FLEET-0004 | runtime | Egress refused personal data going to a region the policy forbids (403). Fix: send it to an allowed region or change the egress policy | fleet-control |
MAIN-FLEET-0005 | runtime | A push does not exist or was already confirmed (404 or 409). Fix: preview a new push | fleet-control |
MAIN-FLEET-0007 | runtime | The deploy gate refused a release that lacks a required mark (403). Fix: run the missing step, then deploy again | fleet-control |
MAIN-FLEET-0008 | runtime | A provider is switched off by a fleet kill switch and declares no fallback (503). Fix: wait for the kill to lift or declare a fallback | fleet-control |
MAIN-FLEET-0009 | runtime | Fleet actions are not active for this tenant: no Fleet contract and no incident mode (402). Fix: activate incident mode or sign a Fleet contract | fleet-defend |
MAIN-FLEET-0010 | runtime | A build includes a package the threat feed marks compromised, and the intel mode is block (403). Fix: update the package, or set the intel start mode to warn | fleet-defend |
MAIN-FLEET-0011 | runtime | An evidence bundle was asked for a standard Fleet does not map (400). Fix: use one of the listed standards | fleet-prove |
MAIN-FLEET-0012 | runtime | An enrolment or report was refused: no valid proof from device management, a registered workload identity, the tenant's DNS or a vendor consent, or the report is not signed by the app's key (403). Fix: present proof from a channel the tenant controls; the app runs normally without Fleet | fleet-enrolment |
MAIN-FLEET-0013 | runtime | The free Fleet view was opened for an organisation that has not proved its domain (403). Fix: add the TXT record named in the error, then open the view again | fleet-enrolment |
MAIN-FLEET-0014 | runtime | A signed report names hosts that the build provenance does not, so the report is flagged and the app's mark shows as unverified (flag, not a refusal). Fix: rebuild with provenance that lists the hosts, or remove the host | fleet-enrolment |
MAIN-FLEET-0015 | runtime | A surface was exported to a format Fleet does not write (400). Fix: use k8s-networkpolicy, vercel-firewall or aws-sg | fleet-discover |
MAIN-FLEET-0016 | runtime | A package query is not name@version (400). Fix: write the query as name@version, such as lodash@4.17.20 | fleet-observe |
MAIN-GEO-0001 | runtime | A GeoHint was passed into a policy, access or auth.require context (400) | geo |
MAIN-GEO-0003 | runtime | geo.geocode was called with an empty address or one over 512 characters, or the driver has no geocoder (platform-headers) (400) | geo |
MAIN-GEO-0004 | runtime | The geocoding driver has no host that serves the tenant's region, so the address is not sent (400) | geo |
MAIN-GEO-0006 | runtime | A geocoding provider could not answer: down, refused, slow past geo.timeout or unreadable (502). geo.geocode catches it, logs one line without the address and returns null | geo |
MAIN-GUARD-0003 | runtime | A blast-radius ceiling refused a step: max-active, block.max-ranges, a range wider than min-prefix or in never, a limit under limit-floor, or a protected target | reaction-safety |
MAIN-GUARD-0004 | runtime | A loop was stopped: the hop limit, the trigger's cause chain already holds this rule's own run, or a neighbour pair cooldown | reaction-safety |
MAIN-GUARD-0006 | runtime | max-per-hour reached; further runs are suppressed and counted, with one alert for the hour | reaction-safety |
MAIN-GUARD-0007 | runtime | A rule is quarantined after a rollback as worse; matches are suppressed until the quarantine ends | reaction-safety |
MAIN-HOOK-0001 | runtime | A webhook call got input it can't use (400): a non-JSON payload, or a bad argument to webhooks.*; or webhooks.emit ran with no signing secret (500). Also the code logged for a delivery attempt that failed with no more specific code. Fix: pass plain JSON, and set webhooks.secret: secret:WEBHOOK_SIGNING_KEY | webhooks-jobs |
MAIN-I18N-0010 | runtime | No pending draft with that id (404). Fix: list drafts with i18n.drafts.list() and use the id of a pending one | i18n |
MAIN-I18N-0011 | runtime | A draft or an export or import request can't run (400): drafting without the ai interface, or an export or import without exactly one target locale and a known format. Fix: configure ai and i18n.ai-draft.model, or pass --locale and a format of xliff, xcstrings, android or icu-json | i18n |
MAIN-I18N-0012 | runtime | A translation provider answered with an error (503, retryable). The app keeps serving the last good messages. Fix: check the provider token and project id | i18n |
MAIN-IDEM-0001 | runtime | Another caller is still running the same idempotent call and holds its key; the wait ran out (409, retryable). Fix: retry shortly; the first call hasn't finished | webhooks-jobs |
MAIN-IDEM-0002 | runtime | An idempotency record couldn't be kept, or one outside a tenant is held only in process (logged). A repeat may run the work again, or a duplicate is caught per instance only. Fix: check the idempotency store, and run the call inside tenant.run | webhooks-jobs |
MAIN-IDENT-0002 | runtime | A wallet presentation was refused: bad signature, untrusted issuer, wrong nonce or audience, missing key binding, altered after binding, or a requested claim not disclosed | digital-ids |
MAIN-IDENT-0003 | runtime | An agent approval (CIBA) was refused: unknown, expired or already answered request, a denial, or a token that exceeds the 5 minute cap or the action scope | digital-ids |
MAIN-IDENT-0004 | runtime | DigiLocker could not verify the person: the token or user call failed, or the response was not usable | digital-ids |
MAIN-JOB-0001 | runtime | jobs.run or jobs.schedule got input it can't use (400): a bad name, an input that isn't JSON or is too large, or an option out of range. Fix: pass plain JSON and the documented options | webhooks-jobs |
MAIN-JOB-0002 | runtime | A job attempt threw, or the job ran out of attempts after its worker stopped (logged per attempt). After the last attempt the job is dead in the dead-letter queue, or failed when dead-letter: false (500). Fix: read the job error, fix the handler and retry the job | webhooks-jobs |
MAIN-JOB-0003 | runtime | A job was claimed whose name has no registered handler (500); it is retried and then dead-lettered. Fix: register a handler for the job name before the worker starts | webhooks-jobs |
MAIN-JOB-0004 | runtime | A cron schedule isn't five valid fields (400). Fix: write five fields, such as 0 3 * * * for 03:00 UTC every day | webhooks-jobs |
MAIN-JOB-0005 | runtime | jobs.dead.retry names no dead job in the current tenant (404). Fix: list the dead-letter queue with jobs.dead.list() | webhooks-jobs |
MAIN-JOB-0006 | runtime | The job queue can't be reached (503, retryable). Nothing was queued or claimed. Fix: check the database the jobs driver uses, then retry | webhooks-jobs |
MAIN-KEYS-0002 | runtime | A data label is refused (400): pan, because card numbers are never stored, or one that isn't personal, sensitive, child or phi. Fix: pass a canonical label, and hand card data to pay | keys |
MAIN-KEYS-0003 | runtime | keys got a call or setup it can't use (400, or 500 for a local master secret under 16 characters): a region that isn't us, eu or in, no KMS key for the region, a record pinned to another region, a decrypt with no purpose, a driver given no client, or rotation on a driver that rotates itself. Fix: add keys.key.<region> to main.yaml, pass a purpose, or use the setting the problem names | keys |
MAIN-KEYS-0005 | runtime | The key service didn't answer, or answered with nothing usable (502, retryable). Fix: check the KMS is reachable and the app may use the key, then retry | keys |
MAIN-KEYS-0006 | runtime | A sealed value, wrapped key or encrypted field failed its integrity check, or isn't a value keys.encrypt made (400): it was changed, moved to another row or field, or sealed under other keys. Fix: use the keys and context it was sealed with, or restore the value from a backup | keys |
MAIN-KEYS-0020 | runtime | The HSM or KMIP server has no key with that label or version (404). Fix: create the key, turn on provisioning, or restore the version | hsm-keys |
MAIN-KEYS-0022 | runtime | An HSM wrapping key is extractable or not sensitive (500). Fix: create it with CKA_EXTRACTABLE false and CKA_SENSITIVE true | hsm-keys |
MAIN-KEYS-0024 | runtime | The KMIP server refused an operation or sent a malformed message (502). Fix: give the app permission to use the key and check the endpoint | hsm-keys |
MAIN-KEYS-0025 | runtime | The customer disabled or revoked its key, so that org's data cannot be read (403). Fix: ask the customer to enable the key; other orgs are unaffected | hsm-keys |
MAIN-MCP-0011 | runtime | A token exchange was refused: no person or agent, no scope, a scope outside mcp.auth.scopes, or a token-ttl over 10 minutes (400) | mcp-actions |
MAIN-MOBILE-0002 | runtime | A write to plain storage (AsyncStorage) used a key that names a secret such as a token or refresh credential; secrets belong in the device vault (Keychain or Keystore) | expo-adapter |
MAIN-NEIGHBOUR-0002 | runtime | The host key or signal folder is unsafe (world-writable, owned by another user and not root) or unreadable; the host bus stays off and the app keeps serving | neighbours |
MAIN-NEIGHBOUR-0003 | runtime | A host envelope failed the host-key check, or claimed a peer's identity and failed its ed25519 check; dropped and counted | neighbours |
MAIN-NEIGHBOUR-0004 | runtime | An ask was refused: the asker isn't a mutual peer, the verb isn't in asks.accept, the asked posture is above max-posture, for is over max-for, or the peer passed max-per-hour | neighbours |
MAIN-NEIGHBOUR-0006 | runtime | A neighbour passed max-per-minute; its host signals were dropped (one record a minute with the count) | neighbours |
MAIN-NEIGHBOUR-0007 | runtime | A signal outside share was asked to go to the host; it stayed in the app | neighbours |
MAIN-NET-0021 | runtime | net.listen, net.listenUnix or a UDP bind on a port, address or path that network.listen doesn't declare, or beyond loopback without a bind in the entry (403 in enforce mode) | net-sockets |
MAIN-NET-0022 | runtime | A TCP connection was closed after idle with no bytes either way | net-sockets |
MAIN-NET-0023 | runtime | An open TCP connection failed: reset by the peer, or a write to a closed peer; never retried | net-sockets |
MAIN-NET-0024 | runtime | A TLS handshake failed: the certificate didn't verify against the roots or the name, the version is below the minimum, or a client certificate was refused or missing (502; names which; never retried) | net-sockets |
MAIN-NET-0026 | runtime | An inbound TCP connection or datagram from a peer the rules don't allow was dropped, or a UDP receive queue was full (logged at most once a minute per source, with a count) | net-sockets |
MAIN-NET-0027 | runtime | A UDP send was refused: over 65,507 bytes, or a broadcast or multicast that network.udp doesn't name | net-sockets |
MAIN-NET-0031 | runtime | A net.listen server was at max-connections; the new connection was closed (logged at most once a minute, with a count) | net-sockets |
MAIN-NET-0032 | runtime | A socket call got a bad argument: a port outside 0 to 65535, a unix path that isn't absolute or is over 104 bytes, data that isn't a Uint8Array, or a write after close (400) | net-sockets |
MAIN-NET-0033 | runtime | net.listenUnix found the socket's directory open to group or others, owned by another user or a symlink, or a file that isn't a socket in the way (500; nothing is bound, and the file is never replaced) | net-sockets |
MAIN-NORMS-0002 | runtime | A metric's source isn't available (no sampling on serverless, a spec not built); the metric is skipped (logged once) | norms |
MAIN-NORMS-0003 | runtime | The stored model is from another format version or is corrupt; it was discarded and learning restarted (logged once) | norms |
MAIN-NORMS-0004 | runtime | norms.delete refused: the caller lacks norms.manage (403) | norms |
MAIN-NOTIFY-0002 | runtime | The call is malformed: no person id, template or category, a missing data value, or a broadcast with no recipients (400) | email-notify |
MAIN-NOTIFY-0005 | runtime | SMS skipped: an India number with no DLT-registered template id; recorded in skipped | email-notify |
MAIN-NOTIFY-0007 | runtime | The channel provider could not deliver the message; the next channel is tried (502) | email-notify |
MAIN-NOTIFY-0008 | runtime | Push skipped: the payload would carry personal data; recorded in skipped | email-notify |
MAIN-NOTIFY-0009 | runtime | The broadcast's rollout state is missing, already finished or cannot move (409) | email-notify |
MAIN-NOTIFY-0010 | runtime | Channel skipped: the daily SMS or WhatsApp cap is reached; recorded in skipped | email-notify |
MAIN-NOTIFY-0011 | runtime | notify.sms or notify.whatsapp is a local channel in production; the app refuses to start (500) | email-notify |
MAIN-OPS-0002 | runtime | A connection to the ops socket or HTTP endpoint had no token or a wrong one; refused and counted | ops-mcp |
MAIN-OPS-0003 | runtime | An HTTP request had a non-loopback Host or Origin, or arrived on a non-loopback address; refused (DNS rebinding guard) | ops-mcp |
MAIN-OPS-0004 | runtime | A write tool was called with ops.mcp.write: off, or a tool name outside the table | ops-mcp |
MAIN-OPS-0006 | runtime | The ops agent's budget is spent; tools answer with this code until the period resets | ops-mcp |
MAIN-PACK-0016 | runtime | A write of a region-locked field to a store outside the region of the app's markets (403). Fix: write the data to a store in the region of your markets | industry-packs |
MAIN-PACK-0017 | runtime | An analytics or ad call for a child without verified parental consent (403). Fix: collect verifiable parental consent first, or do not track this person | industry-packs |
MAIN-PAY-0033 | runtime | pay.driver names a driver that isn't built yet; retired, an unbuilt driver now raises MAIN-SDK-0003. Fix: Use local or stripe until it ships | pay |
MAIN-PERM-0006 | runtime | A write would take a folder past its max (507 Insufficient Storage) | app-permissions |
MAIN-PERM-0007 | runtime | A write was refused because the filesystem's free space is under permissions.disk.reserve (507); logged once per change | app-permissions |
MAIN-PILOT-0001 | runtime | The organisation has not published its DNS proof record; the problem detail shows the record to add (403) | fleet-pilots |
MAIN-PILOT-0002 | runtime | A pilot request is not valid: a missing or malformed organisation, region, date or id (400) | fleet-pilots |
MAIN-PILOT-0003 | runtime | The project is not owned by the tenant's verified organisation (403) | fleet-pilots |
MAIN-PILOT-0004 | runtime | A contract can't be activated: the SOC 2 Type I report is missing or out of date, or a Fleet contract has no Type II report (409) | fleet-pilots |
MAIN-PILOT-0005 | runtime | A delegated role is invalid: no keys, a threshold out of range, or a MainSDK key in the role (400) | fleet-pilots |
MAIN-PILOT-0006 | runtime | Only MainSDK staff may run pilot commands (403) | fleet-pilots |
MAIN-PILOT-0007 | runtime | No pilot tenant with that id (404) | fleet-pilots |
MAIN-PILOT-0008 | runtime | MainSDK Cloud could not issue the pilot licence: no issuer is configured, or the service refused (503 or 502) | fleet-pilots |
MAIN-PLAN-0011 | runtime | A plan or grant the call names doesn't exist (404), or an entitlements call got arguments it can't use (400). Fix: use a plan declared under plans, or list the tenant's grants for the id | entitlements |
MAIN-PLAT-0003 | runtime | A collaboration call was refused because the user may not read or write the document; nothing is sent to them | platforms |
MAIN-PLAYBOOK-0004 | runtime | A run started, planned, waited, applied, rolled back, expired or was declined; the record carries the run | playbooks |
MAIN-PLAYBOOK-0005 | runtime | A step failed while applying; earlier steps of the run were rolled back | playbooks |
MAIN-PLAYBOOK-0006 | runtime | A run was rolled back because errors or latency got worse after it (rollback-if); the rule is quarantined (spec reaction-safety) | playbooks |
MAIN-PLAYBOOK-0007 | runtime | An approval for a run was not answered within approve-timeout (default 30m); the run ended as declined | playbooks |
MAIN-PLAYBOOK-0008 | runtime | playbooks.revert or playbooks.run refused: the caller lacks playbooks.manage or a fresh phishing-resistant session (403) | playbooks |
MAIN-PLAYBOOK-0009 | runtime | The runtime store didn't answer; app-scope overlays applied on this instance only (logged once a minute) | playbooks |
MAIN-POLICY-0001 | runtime (load) | A Cedar or Rego policy has a syntax error; the message names the line | policy |
MAIN-POLICY-0002 | runtime | policy.explain was given a decision id that is not remembered | policy |
MAIN-POLICY-0003 | runtime | A decision input lacks its subject, action or resource | policy |
MAIN-POLICY-0005 | runtime (load) | A rollout's stages are not shadow or percentages from 0 to 100, lowest first | policy |
MAIN-POLICY-0006 | runtime | The browser client's server route did not answer or refused a decision; the action stays denied | policy |
MAIN-POLICY-0012 | runtime (load) | A Rego rule uses a construct the in-process subset does not support; it names the construct and line | policy |
MAIN-PRIV-0002 | runtime | A privacy call got a missing or malformed argument: a user id, a reason, a purpose name, an until date or a consent signal object (400) | privacy-lifecycle |
MAIN-PRIV-0003 | runtime | The person is under a legal hold, so privacy.erase is refused (423) | privacy-lifecycle |
MAIN-PRIV-0005 | runtime | A consent string is not a TCF v2 or GPP string this module reads (400) | privacy-lifecycle |
MAIN-PRIV-0006 | runtime | An erasure is partial: a driver has not confirmed after its retries (logged as a warning, never thrown) | privacy-lifecycle |
MAIN-REALTIME-0018 | runtime | Subscribe or upgrade refused because the app is draining (503) | realtime |
MAIN-REPORT-0001 | runtime | An error group is new in this release (warning record; raises the errors alert source) | error-reporting |
MAIN-REPORT-0002 | runtime | An error group passed alert.spike in its window (raises the errors alert source) | error-reporting |
MAIN-REPORT-0003 | runtime | The error driver couldn't deliver; events are dropped with a count and never block (logged once per outage) | error-reporting |
MAIN-REPORT-0004 | runtime | A browser report was refused: browser is off (404), the body is over 16 KB (413), or the sender passed the rate limit (429) | error-reporting |
MAIN-REPORT-0006 | runtime | The process hit an uncaught exception or unhandled rejection; it was captured as fatal, flushed for at most 2 s, and the process exits with code 1 | error-reporting |
MAIN-REPORT-0007 | runtime | log.errors.get found no group with that fingerprint (404) | error-reporting |
MAIN-SANITIZE-0001 | runtime | Input over maxBytes, or nested deeper than 100 elements (400; nothing is returned) | sanitize |
MAIN-SANITIZE-0002 | runtime | An option asks for a tag, attribute or URL scheme that is never allowed (script, on*, javascript:); thrown when the options are first used | sanitize |
MAIN-SDK-0003 | runtime | The driver named in main.yaml isn't built or installed for this interface; fix names the drivers that are. Replaces MAIN-FILES-0014, MAIN-AI-0013, MAIN-PAY-0033 and that use of MAIN-CONFIG-0003 | cohesion |
MAIN-SDK-0004 | runtime | The runtime store didn't answer within runtime.store.timeout; the caller's on-store-down policy was applied (ADR-0050) | cohesion |
MAIN-SDK-0005 | runtime | A call needs a context value (user, agent or region) that isn't set; fix names the adapter or context.run | cohesion |
MAIN-SDK-0006 | runtime | A duration, size, rate or money value doesn't parse; fix gives the accepted forms. Replaces the other uses of MAIN-CONFIG-0003 and MAIN-NET-0012 | cohesion |
MAIN-SDK-0007 | runtime | A connection to a backing service (the RESP client behind Redis) closed, timed out, was aborted or the server refused a command; retryable is true for the connection cases and fix names the host to declare. Replaces the plain Error throws in core/resp.ts | cohesion |
MAIN-SECRET-0006 | runtime | A secret name that is not upper-case letters, digits and underscores, or a secrets block missing a setting its driver needs | config-secrets |
MAIN-SECRET-0008 | runtime | The secrets provider is unreachable and no earlier value is held, so the read cannot be served | config-secrets |
MAIN-SECRET-0009 | runtime | The secrets provider refused the app's identity (a 401 or 403) | config-secrets |
MAIN-SECRET-0010 | runtime | A secret is past 80% of its max-age (warn, once a day per secret) | secret-rotation |
MAIN-SECRET-0012 | runtime | mainsdk secrets rotate on a secret without generate, or on a driver without write; prints the provider steps instead | secret-rotation |
MAIN-SECRET-0013 | runtime | A secret watcher threw; the others still ran | secret-rotation |
MAIN-SECURITY-0003 | runtime | The export buffer was full; events below high were dropped (one record per minute with the count) | security-events |
MAIN-SECURITY-0004 | runtime | A security.on subscriber threw; the others still ran | security-events |
MAIN-SECURITY-0005 | runtime | security.emit called with a type outside app.*, or with an attribute not in telemetry@1 (the type is refused; the attribute is dropped) | security-events |
MAIN-SECURITY-0010 | runtime | Brute force: one account failed sign-in failures times within window | security-events |
MAIN-SECURITY-0011 | runtime | Credential stuffing: one source failed sign-in on accounts different accounts within window | security-events |
MAIN-SECURITY-0012 | runtime | Access probing: one actor was denied denials different resources within window | security-events |
MAIN-SIGNAL-0001 | runtime | signals.emit refused: the name isn't app.*, doesn't match the name pattern, the severity is unknown, or the data isn't JSON or is over 4KB | signals |
MAIN-SIGNAL-0002 | runtime | A signal's data attribute isn't in telemetry@1 or matched a secret or personal-data detector; the attribute was dropped and named | signals |
MAIN-SIGNAL-0003 | runtime | A source passed max-per-minute; further emits were dropped (one record a minute with the count) | signals |
MAIN-SIGNAL-0004 | runtime | An app-scope or host-scope envelope failed verification (no signature where one is required, a bad signature, an unknown key id, a stale or future at); it was dropped and counted | signals |
MAIN-SIGNAL-0005 | runtime | A signals.on handler threw or took longer than 1s; the others still ran | signals |
MAIN-SIGNAL-0008 | runtime | A handler's queue was full; the oldest signals were dropped (one record a minute with the count) | signals |
MAIN-SYNC-0001 | runtime | A row was refused: the change names another tenant (the push fails, 403) or the person may not write the row (the row is reported and the rest goes through) | sync |
MAIN-SYNC-0002 | runtime | The device's schema version of a table differs from sync.tables in main.yaml (409); the device keeps its queue | sync |
MAIN-SYNC-0003 | runtime | The tenant is over sync.quota for rows, bytes or the size of one push (429); the push changes nothing | sync |
MAIN-SYNC-0004 | runtime | A change or call is not well formed, or names a table that is not in sync.tables (400) | sync |
MAIN-SYNC-0005 | runtime | A change carries a clock value further ahead of the server's clock than sync.max-clock-drift; that row is reported and dropped | sync |
MAIN-SYNC-0006 | runtime | The person was erased and can no longer sync (410); the device wipes its copy and queue | sync |
MAIN-TENANT-0008 | runtime | tenant.create or auth.orgs.create got input it can't use: no name, a name over 120 characters, a region other than us, eu or in (400), or an id or slug that is invalid or taken (409). Fix: pass { name, region }, and an id such as tnt_acme or a slug nobody has | tenancy |
MAIN-TESTING-0001 | runtime | An interface in the catalog has no in-memory fake in the test kit, or its fake does not resolve | testing-kit |
MAIN-TESTING-0002 | runtime | A chaos drill names a target the kit does not know, or a helper that acts on the current test app runs before one exists | testing-kit |
MAIN-TESTING-0003 | runtime | A driver cannot be tested: it is not a defineDriver module, names another interface than it implements, or declares no manifest | testing-kit |
MAIN-TESTING-0004 | runtime | A conformance runner broke the line protocol: it printed a line that is not JSON, answered a case it was not asked, or exited without answers | testing-kit |
MAIN-TESTING-0005 | runtime | The testing kit is not installed in the project, or the main.yaml schema it exports vectors from is not built | testing-kit |
MAIN-TRUST-0001 | runtime | A Fleet contract cannot be activated because the trust page lacks required items (409). Fix: complete the missing items, then activate | mainsdk-trust |
MAIN-TRUST-0002 | runtime | A staff access to tenant data in the quarter did not use SSO, MFA and step-up (500). Fix: find how the access was made and fix the access path | mainsdk-trust |
MAIN-TUF-0002 | runtime | Metadata is malformed, or lacks the threshold of signatures its role needs | tuf-delivery |
MAIN-TUF-0003 | runtime | A target's bytes differ from the length or hash in the signed targets; the bytes are never handed out | tuf-delivery |
MAIN-TUF-0005 | runtime | A target is not listed in the current signed targets and snapshot (mixed release, unknown name, or not delivered yet) | tuf-delivery |
MAIN-TUF-0006 | runtime | A delegated role signed a path outside its delegation | tuf-delivery |
MAIN-TUF-0007 | runtime | Root cannot be trusted or updated: no installed root, or a new root not signed by both the old and the new keys | tuf-delivery |
MAIN-TUF-0008 | runtime | Refreshed: fresh metadata verified (logged at info) | tuf-delivery |
MAIN-TUF-0009 | runtime | The mirror is unreachable but the cached metadata has not expired (logged at info) | tuf-delivery |
MAIN-UPDATE-0001 | runtime | The client's version is below updates.min-version, or it named none under strict (426; the problem carries min_version) | updates |
MAIN-UPDATE-0002 | runtime | A version, platform or channel in a request or call isn't valid (400) | updates |
MAIN-VAULT-0001 | runtime | The vault has no location for the person's country, or its driver does not support it (422). Fix: add the country to vault.locations or pick a driver that supports it | residency-vaults |
MAIN-VAULT-0002 | runtime | The vault returned no token, alias or record for a write (502, retryable). Fix: retry, and check the vault credentials and location | residency-vaults |
MAIN-VAULT-0003 | runtime | A field marked vault: true has the label pan or a label a vault cannot hold (400). Fix: use personal, sensitive, child or phi; hand card data to pay | residency-vaults |
MAIN-VAULT-0004 | runtime | A blind index was asked of a vault driver that has none (400). Fix: look the person up by an id the app holds | residency-vaults |
MAIN-VAULT-0005 | runtime | The vault has not confirmed an erasure (502, retryable). Fix: wait for the erasure to retry; check the vault if it stays pending | residency-vaults |
MAIN-VERIFY-0001 | runtime | A code or link did not check out: unknown, expired, used, wrong, or out of tries. The call answers ok: false and the audit record carries this code; nothing says which | verify |
MAIN-VERIFY-0002 | runtime | Too many asks for one number, address or IP, or the bot-defence door or the pumping check said no (429); nothing was sent | verify |
MAIN-VERIFY-0003 | runtime | The provider could not deliver and said nothing about the recipient: an outage, a timeout or a rate limit (502, retryable) | verify |
MAIN-VERIFY-0005 | runtime | A number is not a valid E.164 number, or an address is not an email address (400) | verify |
MAIN-VERIFY-0007 | runtime | An identity call is malformed: unknown level, no market, person, purpose, claims or consent (400) | verify |
MAIN-VERIFY-0008 | runtime | An identity provider could not be reached or answered something unusable (502, retryable) | verify |
MAIN-VOICE-0006 | runtime | The agent couldn't join the room: the provider's API refused it, the signalling socket didn't confirm the room and identity, or the media bridge refused the token or didn't say it had joined in time (502, retryable) | ai-voice |
MAIN-WEB-0002 | runtime | Cross-site request refused: an unsafe method carried a cookie from another site, or no Fetch Metadata, no matching Origin and no valid token (403) | inbound-protection |
MAIN-WEB-0004 | runtime | A handler set a security header itself; the declared value replaced it (logged once per route and header; the fix is an inbound.routes entry) | inbound-protection |
MAIN-WEB-0005 | runtime | CORS preflight from an origin not in cors.origins, or with a method or header not allowed (403, no CORS headers) | inbound-protection |
MAIN-WEB-0006 | runtime | A CSP violation report arrived (warn; sampled to one record per directive and blocked URL per minute) | inbound-protection |
MAIN-WEB-0008 | runtime | A response set a cookie without Secure, HttpOnly or SameSite; the adapter added them (logged once per cookie name) | inbound-protection |
MAIN-WEB-0012 | runtime | security.txt expires within 30 days (warn, once at start and once a day) | inbound-protection |
MAIN-WEB-0013 | runtime | inbound.nonce() or inbound.csrfToken() was called outside a request (500) | inbound-protection |
MAIN-WORKER-0002 | runtime | A worker module that workers.modules or a pool's modules doesn't declare (403 in enforce mode) | parallelism |
MAIN-WORKER-0003 | runtime | A task's input or result can't be cloned (a function, a symbol, a class with private state); names the path; nothing was sent (400) | parallelism |
MAIN-WORKER-0004 | runtime | A task's input holds a resolved secret; nothing was sent, and the value is never echoed (403) | parallelism |
MAIN-WORKER-0005 | runtime | A task passed its timeout or the context's deadline; its thread or process was terminated and replaced (504) | parallelism |
MAIN-WORKER-0006 | runtime | A task's thread or process ran out of memory, exited or crashed; it was replaced (500) | parallelism |
MAIN-WORKER-0007 | runtime | The pool's queue is full (503, retryable, retryAfter 1) | parallelism |
MAIN-WORKER-0008 | runtime | A call inside a worker module to an interface that isn't available there (secrets, network, sockets, disk, db, jobs and the rest); names the call (500) | parallelism |
MAIN-WORKER-0011 | runtime | A cluster instance exited without being asked; it is respawned after the backoff (logged with slot, exit code and signal) | parallelism |
MAIN-WORKER-0012 | runtime | A cluster slot crashed crashes times within window; the primary stopped respawning it, and exits 1 when no instance is left | parallelism |
MAIN-WORKER-0013 | runtime | A rolling restart stopped: a new instance wasn't ready within start-timeout; the old instances keep serving | parallelism |
MAIN-WORKER-0014 | runtime | Parallelism isn't available here, so tasks run inline or the cluster runs one process; logged once with the reason (runtime, mainsdk dev, serverless) | parallelism |
MAIN-WORKER-0015 | runtime | Processes times heap limit, plus worker threads times memory, is above the container's memory limit; names the --max-old-space-size to set per process (logged once at start) | parallelism |
MAIN-WORKFLOW-0013 | runtime | workflow() got a name that isn't lowercase letters, digits, dots, dashes and underscores, or a version that isn't a positive whole number (400). Fix: write a valid name and version: 1, then 2 when the steps change | workflows |
Debug an error
$ npx mainsdk explain 4bf92f3577b34da6a3ce929d0e0e4736
MAIN-DATA-0007 Record belongs to another user
at app/api/orders/[id]/route.ts:18 (getOrder)
rule data.owner_scope
fix Scope the query with db.orders.for(user).mainsdk explain works offline from the local registry and event store. To see each provider call (driver, operation, duration, status, with bodies and headers redacted), set MAINSDK_DEBUG=driver. In production it is refused with MAIN-LOG-0003 unless a debug session allows it; see environment variables.
Errors from MainSDK Cloud
When the Free allowance runs out after the warning and grace period, MainSDK Cloud answers 402. Only the Cloud: your app keeps running and every check keeps working.
{ "type": "https://mainsdk.dev/errors/MAIN-CLOUD-0402", "title": "Free allowance used",
"status": 402, "detail": "50,000 events used this month. Add a card or move to Teams.",
"upgrade": "https://mainsdk.dev/billing?prj=prj_01JC8Q4W7ZK3M5N8P2R6T9V0XY" }Next
Was this page helpful?