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.

```text
workerd: "secret not found" for API key binding at runtime
```

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

2. List what is actually stored on the deployed worker. Run:
   ```bash
   wrangler secret list
   ```
   Expected: a table of secret names that includes the name from step 1. If it is missing, that is your bug.

3. Upload the secret with the exact same name. Run:
   ```bash
   wrangler secrets put MY_KEY
   ```
   and paste the value when prompted. Replace MY_KEY with the real name.
   Expected: a success message confirming the secret was uploaded.

4. If you deploy with named environments, repeat for each environment:
   ```bash
   wrangler secrets put MY_KEY --env staging
   ```
   Expected: the secret appears under that environment too. Secrets are per environment, not shared.

5. Redeploy and exercise the code path that reads the secret. Run:
   ```bash
   wrangler deploy
   ```
   then 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. MY_KEY and my_key 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 delete` removes 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
