Skip to content

Structured errors

In --json mode, every failure returns a structured envelope instead of a free-form error message. Agents can branch on error.type, error.code, and error.retryable without parsing English text.

{
"ok": false,
"command": "wd deploy",
"error": {
"type": "auth",
"code": "WD_E_ACCOUNT_MISMATCH",
"message": "Cloudflare API error 10000: account mismatch",
"retryable": false,
"fix": "Set CLOUDFLARE_ACCOUNT_ID to match the account that owns your CLOUDFLARE_API_TOKEN.",
"expected": { "env": ["CLOUDFLARE_ACCOUNT_ID"] },
"suggestions": [
"Set CLOUDFLARE_ACCOUNT_ID to match the account that owns your CLOUDFLARE_API_TOKEN.",
"Run `wd doctor --json --codes` to verify auth."
]
}
}

Get the schema directly:

Terminal window
wd schema errors --json
FieldTypeNotes
okfalseAlways false on errors. Lets agents branch with one check.
commandstringThe command name, e.g., "wd deploy".
error.typeenumOne of auth, validation, network, config, state, not_found, permission, sandbox, unknown.
error.codestringStable identifier prefixed with WD_E_. Safe to switch on.
error.messagestringHuman-readable summary. May contain [REDACTED] if --no-secrets-in-output is on.
error.retryablebooleanIf true, the same call is worth retrying (e.g., transient network).
error.fixstring?One concrete remediation step.
error.expectedunknown?Structured “what was expected” — e.g., { env: ["CLOUDFLARE_API_TOKEN"] } or { command: ["apply", "deploy", ...] }.
error.suggestionsstring[]?Additional remediation hints, including “run wd explain WD_E_X”.
CodeTypeRetryableWhen
WD_E_STATE_MISSINGstatenoStage has no state. Run wd apply --stage <name>.
WD_E_ACCOUNT_MISMATCHauthnoAPI token / account ID mismatch.
WD_E_AUTH_FAILEDauthnoToken rejected, or required env vars missing.
WD_E_CONFIG_MISSINGconfignoNo wrangler-deploy.config.ts in scope.
WD_E_DEPS_MISSINGconfignoThe config imports a package that isn’t installed.
WD_E_NOT_FOUNDnot_foundnoPath or named resource not found.
WD_E_NETWORKnetworkyesTransient network error. Retry safe.
WD_E_VALIDATIONvalidationnoBad/missing flag or argument.
WD_E_PERMISSIONpermissionnoFilesystem permission denied.
WD_E_SANDBOX_BLOCKEDsandboxnoMutating command refused under AGENT_SANDBOX=1 without --dry-run.
WD_E_UNKNOWNunknownnoUnclassified — use wd explain --from-last-error.

The full live list is at wd schema errors --json.

Exit codeMeaning
0Success
1Runtime failure (network, state, config, auth, unknown)
2Validation or sandbox refusal — your inputs/environment are wrong, not the system

Agents can branch on exit code first, then read the envelope only when needed.

const proc = spawnSync("wd", ["deploy", "--stage", "staging", "--json"], {
encoding: "utf-8",
});
if (proc.status === 0) {
const result = JSON.parse(proc.stdout);
return result;
}
const envelope = JSON.parse(proc.stdout);
if (envelope.error.retryable) {
await sleep(2000);
return retry();
}
switch (envelope.error.type) {
case "auth":
// surface a re-auth prompt
break;
case "state":
// chain `wd apply --stage staging --json` first, then retry deploy
break;
case "validation":
// your call was malformed — inspect error.expected
throw new Error(envelope.error.fix);
case "sandbox":
// running under AGENT_SANDBOX without --dry-run; either drop sandbox or add --dry-run
break;
}

Every failure is also written to .wrangler-deploy/last-error.json (best-effort). You can ask the CLI for guided remediation without needing to keep the original error in scope:

Terminal window
wd explain --from-last-error --json
wd explain --error-code WD_E_STATE_MISSING --json

Almost every failure path in wrangler-deploy throws an AgentErrorException carrying the structured payload directly — the catch handler skips classification and emits the envelope verbatim. The few remaining un-migrated sites are wrappers around downstream library errors (Wrangler subprocess output, encryption errors, file system errors); those still classify correctly via message-pattern regex.

If you’re contributing code, prefer the typed helpers over throw new Error:

import { AgentErrors, assertStage, assertStageState, assertUsage } from "./cli-output.js";
assertStage(stage); // missing --stage
assertStageState(state, stage); // null state
assertUsage(workerPath, "Usage: wd foo --worker X"); // missing required arg
throw AgentErrors.notFound("File not found: x.json", "Check the path.");
throw AgentErrors.auth("Token rejected", "Run `wd login`.", { env: ["CLOUDFLARE_API_TOKEN"] });
throw AgentErrors.network("ECONNREFUSED talking to api.cloudflare.com");

Each helper returns never and the assertX variants narrow types via TypeScript’s asserts so the surrounding code doesn’t need null checks.