P PasteCode
Failure

How to Fix AI Forgetting Environment Variable Validation

AI agents read process.env values directly without validation, causing silent undefined bugs and missing startup errors when env vars are absent.

CursorClaude CodeCodexWindsurf Next.jsTypeScriptCloudflare
.md .json Updated Jun 8, 2026

The agent accesses process.env.SOME_KEY directly, so when the variable is missing the app starts silently broken — no crash, no warning, just undefined flowing through business logic.

The symptom

Raw process.env reads scattered across the codebase with no schema, no type safety, and no startup assertion.

// lib/stripe.ts — WRONG
import Stripe from "stripe";
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: "2024-06-20",
});
// If STRIPE_SECRET_KEY is undefined, Stripe SDK accepts it and every
// charge silently fails at runtime instead of at startup.

Why it happens

The agent optimizes for brevity and reaching working code fast. Env validation is boilerplate that doesn’t appear in most training examples, so the model skips it and goes straight to the integration.

How to spot it

  • process.env.FOO used without a non-null assertion or fallback check.
  • No env.ts / env.mjs validation file in the project.
  • TypeScript type of an env value is string | undefined at the call site.
  • App boots without error even when .env.local is empty.

How to fix it

Validate all required env vars at startup using a Zod schema so the process crashes with a clear message before serving a single request.

// lib/env.ts — CORRECT
import { z } from "zod";
const envSchema = z.object({
STRIPE_SECRET_KEY: z.string().min(1),
DATABASE_URL: z.string().url(),
NEXTAUTH_SECRET: z.string().min(32),
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
});
export const env = envSchema.parse(process.env);
// ^^^^^ throws at startup if any var is missing
// lib/stripe.ts — CORRECT (import validated env)
import Stripe from "stripe";
import { env } from "@/lib/env";
export const stripe = new Stripe(env.STRIPE_SECRET_KEY, {
apiVersion: "2024-06-20",
});
[ ] Create lib/env.ts with a Zod schema covering every required variable
[ ] Import env from lib/env everywhere — never use process.env directly
[ ] Add lib/env.ts to the module graph so it runs at server startup (import in next.config.ts)
[ ] Document all variables in .env.example with placeholder values
[ ] Use z.string().url() / z.string().min(n) for format constraints, not just presence

Fix Prompt

Fix Prompt
Every raw process.env access in this file is unvalidated. Create a lib/env.ts
module that parses and validates all required environment variables with Zod at
startup. Replace every process.env.FOO reference in the codebase with the
typed env.FOO import. Add a .env.example file listing every variable with a
placeholder value and comment.

Test

Terminal window
# Find any remaining raw process.env reads outside of lib/env.ts
grep -rn "process\.env\." --include="*.ts" --include="*.tsx" . \
| grep -v "lib/env.ts" \
| grep -v "next.config" \
| grep -v "node_modules" \
&& echo "FAIL: raw process.env reads found" || echo "OK"