Troubleshooting
Symptom → cause → fix for the most common Tamer failure modes. For background on each command, see Lifecycle.
TamerReferenceError: unresolved reference … (at <field>)
Cause: a ${tamer:…} reference (or cf.…() binding) couldn't be resolved against state — the referenced resource hasn't been created/recorded yet, or the logical name has a typo. In strict commands (apply, deploy, destroy) this throws; in tolerant commands (plan, drift, status, sync) the placeholder is left in place.
Fix:
- For a resource in this stack on a remote env:
tamer apply --env <env>first (it creates the resource and records the ID in state). - On
--env local,${tamer:import:…}is empty and there is no state row. Put local-only values in the workerlocaloverlay (or basevars). - For a
${tamer:import:<stack>.<output>}cross-stack reference:applythe producer stack in the same env first so itsoutputsrow exists. - Check the logical name spelling against your config (the error includes the field path).
StateConflictError (exit code 3)
Cause: tamer-state was modified by another writer between when Tamer read it (hydrate) and when it tried to commit — concurrent apply/deploy on the same env+stack, or an in-flight operation didn't finish. Exit code 3 (not
- distinguishes this from a generic failure.
Fix: re-run the command — Tamer re-reads the current revision and retries. Avoid running two mutating commands against the same env+stack at once.
tamer apply --plan plan.json refuses with an attestation mismatch
Cause: apply --plan recomputes the (config, state) attestation hashes pinned in the plan file and aborts if either changed since tamer plan --out ran. This prevents applying a stale plan to a changed world.
Fix:
- Re-run
tamer plan --env <env> --out plan.json, thenapply --plan. - If the change is expected and you intentionally want to proceed, override with
--allow-stale.
tamer destroy / wfp tenant destroy blocked by protected-env gate
Cause: the env is in protectedEnvs (default ["prod", "production"], configurable on the root config). Protected envs require explicit confirmation to prevent accidental teardown. Same gate on tamer reset and wfp tenant reset.
Fix:
# Stack destroy / reset — type the env name
tamer destroy --env prod --confirm-env prod
tamer reset --env prod --confirm-env prod --target d1:app-db
# WFP tenant destroy / reset — type the workspace
tamer wfp tenant destroy --env prod --workspace acme --confirm-tenant acme
# Break-glass: skip the confirmation gate only (same deletions either way)
tamer destroy --env prod --force--force does not change what gets deleted — it only skips the “type the env / tenant name” check. Ephemeral pr-* envs are usually not protected, so they need neither flag unless you added them to protectedEnvs.
Widen the gate with protectedEnvs: ["prod","production","qa"] or opt out with []. See Lifecycle.
Workflow deferred (script "..." not deployed yet; will register during deploy)
Cause: not an error. On a greenfield env, apply runs before the host script is deployed, so Cloudflare can't register the Workflow yet. Tamer defers registration and completes it during deploy after wrangler deploy succeeds.
Fix: none — proceed to tamer deploy. If it persists after a successful deploy, re-run tamer deploy --env <env>.
tamer deploy fails: declared secret missing from the vault
Cause: a name in secrets.required has no value in the tamer-secrets vault for that env. Deploy aborts before pushing secrets.
Fix:
echo -n "sk_live_..." | tamer secrets set STRIPE_API_KEY --env dev
tamer secrets verify --env dev # confirm all required secrets are present
tamer deploy --env devFor ephemeral PR envs, tamer secrets copy --from dev --to pr-42 seeds the vault in one step. See Secrets.
tamer config.ts is not supported
Cause: a file literally named tamer.config.ts is rejected by the CLI (in both discovery and --config).
Fix: move the default export to tamer/project.config.ts (nested layout) or tamer.project.config.ts (flat layout). See Project Config.
State and reality disagree after out-of-band changes
Symptom: someone created/deleted a resource in the dashboard, or ran wrangler directly, so tamer-state no longer matches Cloudflare.
Fix:
tamer sync --env dev # merge live Cloudflare listings into state (no writes)
tamer drift --env dev # read-only diff: state vs CF vs configRe-run sync after any out-of-band change. drift reports missingFromCloudflare, unrecordedInState, and undeployed.
Reviewing what happened: tamer events
tamer events --env dev # operation timeline (last 50)
tamer events --env dev --limit 10 # fewer rows
tamer events --env dev --json # machine-readableEach mutating command (bootstrap, apply, deploy, destroy, import) appends a snapshot to operationHistory. Use this to see who ran what and whether it succeeded.
Verifying credentials: tamer doctor
tamer doctor --env dev
tamer doctor --env dev --jsonChecks CLOUDFLARE_ACCOUNT_ID / CLOUDFLARE_API_TOKEN against the Cloudflare API. Run this first if commands fail with auth errors.
wrangler.json missing / Missing entry-point / Wrangler asks for a config
Cause: Tamer does not commit Wrangler config. Nothing exists until you generate it.
Fix: stack workers → tamer wrangler --env local. WFP tenant templates → tamer wfp tenant wrangler --env local --workspace <ws>. Then run wrangler dev from the worker directory (or pass --config). Do not hand-author wrangler.toml. See Local Development.
tamer wrangler requires --env local
Cause: tamer wrangler / wfp tenant wrangler are generate-only for local. Remote wrangler.json is written by tamer apply --env <env> / tamer deploy.
Fix: drop --env dev (or pass --env local). For a real env, run apply / deploy.
tamer apply --env local did not create a D1 / R2 / KV
Cause: local apply is generate-only. It writes the wrangler pair and does not call the Cloudflare API.
Fix: for laptop Miniflare, that is expected — tamer migrate --env local (or wfp tenant migrate) against the generated primary. For a real database, tamer apply --env dev (needs CLOUDFLARE_*).
Vitest hits a real R2 / needs CLOUDFLARE_*
Cause: tests pointed at wrangler.json, which may keep remote: true from a local overlay.
Fix: wranglerConfigPath: "./wrangler.vitest.json" (sibling; remote stripped). Regenerate with tamer wrangler if the file is missing.
Stack tamer migrate / reset did nothing for a tenant D1
Cause: stack commands only cover workers.*. Per-tenant product Workers live on wfp.namespaces.*.workers.*.
Fix: tamer wfp tenant wrangler|migrate|seed|reset --env local --workspace <ws>.
CLOUDFLARE_API_TOKEN required on a local command
Cause: you passed a non-local --env, or ran a remote-only command (bootstrap, doctor, plan, deploy, wfp tenant provision, …).
Fix: omit --env or pass --env local on wrangler / migrate / seed / types / apply. Those need no credentials.
Related docs
- Local Development — generate-then-run; no Cloudflare
- Lifecycle — command sequence and what each writes
- Secrets — vault reconciliation states
- CI Workflows —
secrets verify → apply → migrate → deploy - CLI Reference — all flags