workerd: "secret not found" for API key binding at runtime
Fixes the workerd "secret not found" error when a Worker reads an environment secret at runtime. Use it when code like env.MY_KEY returns undefined in production even though it works in wrangler dev. The usual trigger is a secret that was never uploaded with wrangler secrets put for the deployed environment, or a name mismatch between the code and the stored secret.
TL;DR: Run wrangler secrets put using the exact name your code reads, for the environment you deployed to, then redeploy. Local dev reads secrets from .dev.vars while production reads what was uploaded, so a missing upload or a name mismatch leaves workerd with nothing at runtime.
workerd: "secret not found" for API key binding at runtime- Confirm the exact name your code reads. Search your source for the env access, for example
env.MY_KEY, and write down the name character for character.
Expected: one consistent name across your code and your docs.
- List what is actually stored on the deployed worker. Run:
wrangler secret listExpected: a table of secret names that includes the name from step 1. If it is missing, that is your bug.
- Upload the secret with the exact same name. Run:
wrangler secrets put MY_KEYand paste the value when prompted. Replace MY_KEY with the real name. Expected: a success message confirming the secret was uploaded.
- If you deploy with named environments, repeat for each environment:
wrangler secrets put MY_KEY --env stagingExpected: the secret appears under that environment too. Secrets are per environment, not shared.
- Redeploy and exercise the code path that reads the secret. Run:
wrangler deploythen trigger the request and check the logs. Expected: the code reads the value instead of throwing.
Use this when
- env.SOMETHING is undefined or throws in production but works in
wrangler dev - logs show "secret not found" at runtime
- you set the value in .dev.vars locally and assumed it carried over to deploy
- you use named environments (staging, production) and the secret only exists in one of them
- the secret name has different casing or spelling in code vs the dashboard
Not for this skill when
- the failure happens at build or deploy time rather than at request time
- the error is about KV, D1, R2, or Durable Object bindings rather than plain env secrets
- your code reads process.env, which workerd does not provide without the Node.js compatibility flag
- wrangler itself rejects your credentials during login or deploy (that is an auth problem, not a missing secret)
Variant phrasings
- "secret not found" workerd
- wrangler secrets put not working, worker still says secret missing
- environment variable undefined in production Cloudflare Worker
- .dev.vars works locally but secret missing after deploy
- env binding returns undefined on deployed worker
Why it happens
wrangler dev loads secrets from a local .dev.vars file on your machine. A deployed worker reads secrets from what was uploaded to Cloudflare's side with wrangler secrets put. Nothing syncs the two automatically, and secrets are scoped per environment. So a name that exists locally but was never uploaded, or was uploaded to staging while you deployed to production, resolves to nothing at runtime and workerd raises "secret not found".
Edge cases
- Secret values are write-only. You cannot read a value back to check it, so if the value itself is wrong you must re-upload it.
- Names are case-sensitive. MYKEY and mykey are different secrets.
- Rotating a secret can take a short time to reach every isolate. A request that hits an isolate started before the rotation may briefly see the old value.
- Secrets set in the dashboard under one environment do not appear in another. Audit each environment separately with
wrangler secret list --env NAME. - Deleting a secret with
wrangler secret deleteremoves it everywhere that environment is deployed, so code that reads it will start throwing again.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_8mFBrhDb3gY3Augq1Jv9sg
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.