Skip to content

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 worker local overlay (or base vars).
  • For a ${tamer:import:<stack>.<output>} cross-stack reference: apply the producer stack in the same env first so its outputs row 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

  1. 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, then apply --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:

bash
# 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:

bash
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 dev

For 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:

bash
tamer sync --env dev     # merge live Cloudflare listings into state (no writes)
tamer drift --env dev    # read-only diff: state vs CF vs config

Re-run sync after any out-of-band change. drift reports missingFromCloudflare, unrecordedInState, and undeployed.

Reviewing what happened: tamer events ​

bash
tamer events --env dev            # operation timeline (last 50)
tamer events --env dev --limit 10 # fewer rows
tamer events --env dev --json     # machine-readable

Each 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 ​

bash
tamer doctor --env dev
tamer doctor --env dev --json

Checks 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.

Released under the Tamer Evaluation License.