Environment variable read but never declared
Code reading an environment variable nothing declares.
Why it matters
An agent adding an integration writes process.env.STRIPE_WEBHOOK_SECRET and moves on. It may have set the value in your local shell, or never set it at all. The example env file, the deploy config and the platform dashboard are outside the task, so they are rarely updated.
The deploy starts fine because nothing reads the variable at boot. It fails later, on the specific request that reaches the new code, and the error looks unrelated to the change. A new developer or a fresh environment hits the same wall with no list of what to set.
Example
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
+const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
+const proPriceId = process.env.STRIPE_PRO_PRICE_ID!;
export async function createCheckout(userId: string) {
return stripe.checkout.sessions.create({
- line_items: [{ price: "price_1PqL", quantity: 1 }],
+ line_items: [{ price: proPriceId, quantity: 1 }],
What it detects
- New reads through process.env, import.meta.env, os.environ, os.getenv, Deno.env.get and Ruby ENV[] in source, config and infrastructure files.
- Each name is looked up in .env.example and its common variants (at the repo root and beside each changed file, for monorepos), env.ts, docker-compose, Dockerfiles, fly.toml, render.yaml, vercel.json, netlify.toml, serverless.yml, Kubernetes and Helm directories, and GitHub workflows.
- Reads with a fallback (?? or || after the read, or getenv with a default) and names set by the platform, such as NODE_ENV, PORT, VERCEL_URL and AWS_REGION, are not flagged.
- Also flags a variable removed from a declaration file while code still reads it.
- It only sees files, so variables injected by a hosting dashboard or secret manager will show up as missing.
What to do when it fires
Add each name to .env.example (names only, no values) and to whatever sets your production environment. Then fail fast at startup when a required variable is missing, so the next gap shows up at deploy time rather than on a user's request.
If the value really is optional, give the read an explicit fallback in code and the check will stop reporting it.