Netlify: env vars not loading in functions [2026 fix]

Env vars fail in Netlify Functions because they're not bundled into the function package. Move vars to netlify.toml [functions] or use context.clientContext.

TL;DR

Cause: Environment variables defined in Netlify UI aren't automatically injected into serverless Functions; they're only available at build-time unless explicitly configured.

Fix: Define function-specific env vars in netlify.toml under [functions] section or access them via context.clientContext in newer runtimes.

---

Real Console Error Messages

``` 1. TypeError: Cannot read properties of undefined (reading 'DATABASE_URL') at Runtime.handler (/var/task/index.js:15:23)

2. ReferenceError: STRIPE_API_KEY is not defined at Object.<anonymous> (/var/task/functions/stripe.js:3:5)

3. Error: env var SECRET_TOKEN returned undefined in function execution at checkEnv (/var/task/middleware.js:42:10)

4. Uncaught SyntaxError: Invalid or unexpected token at process.env.API_URL Context: function logs show 'undefined' when accessing process.env

5. TypeError: Cannot set property AUTH_SECRET, which has only a getter indicates immutable env state in Netlify Functions container ```

---

The Problem: Broken vs Fixed Code

❌ BROKEN: Relying on UI-defined env vars in Functions

```javascript // netlify/functions/api.js exports.handler = async (event, context) => { const dbUrl = process.env.DATABASE_URL; // ← UNDEFINED const apiKey = process.env.STRIPE_KEY; // ← UNDEFINED const response = await fetch(dbUrl); return { statusCode: 200, body: JSON.stringify({ key: apiKey }) }; }; ```

Result at 2am: Function returns undefined for all env vars despite being set in Netlify dashboard.

---

✅ FIXED: Method 1 – netlify.toml configuration

```toml

netlify.toml

[functions] node_bundler = "esbuild" [[functions]] name = "api" external_node_modules = ["lodash"] environment = { DATABASE_URL = "postgresql://user:pass@host/db", STRIPE_KEY = "sk_live_..." } ```

```javascript // netlify/functions/api.js exports.handler = async (event, context) => { const dbUrl = process.env.DATABASE_URL; // ✅ NOW DEFINED const apiKey = process.env.STRIPE_KEY; // ✅ NOW DEFINED const response = await fetch(dbUrl); return { statusCode: 200, body: JSON.stringify({ key: apiKey }) }; }; ```

---

✅ FIXED: Method 2 – context.clientContext (Netlify Runtime v1+)

```javascript // netlify/functions/api.js exports.handler = async (event, context) => { // Access from context injected by Netlify runtime const { DATABASE_URL, STRIPE_KEY } = context.clientContext.custom || {}; if (!DATABASE_URL) { return { statusCode: 500, body: JSON.stringify({ error: "DATABASE_URL not found in context" }) }; } const response = await fetch(DATABASE_URL); return { statusCode: 200, body: JSON.stringify({ key: STRIPE_KEY }) }; }; ```

Note: context.clientContext approach requires Netlify Functions Runtime v1.0+. If uncertain about your runtime version, check your netlify.toml for aws_lambda_memory or test locally with netlify dev.

---

✅ FIXED: Method 3 – Secrets via Build Context (TypeScript example)

```typescript // netlify/functions/secure.ts import { Handler, HandlerContext } from "@netlify/functions";

const handler: Handler = async (event, context: HandlerContext) => { // For secrets, prefer netlify.toml [functions] block // Avoid logging sensitive data const hasToken = !!process.env.AUTH_SECRET; return { statusCode: 200, body: JSON.stringify({ authenticated: hasToken }) }; };

export { handler }; ```

---

Still Broken? Check These Too

1. Build cache issue – Netlify might be serving stale function bundles. Go to Deploy Settings → Trigger Deploy → "Deploy site" (not "Clear cache and deploy" first, that's often ineffective). Wait 2–3 minutes for Functions to redeploy.

2. Function bundling mismatch – If using a custom build command, ensure node_bundler = "esbuild" is set in netlify.toml. Older zisi bundler doesn't always pick up env vars correctly. See [Netlify build environment setup](/?guide=build-env) for details.

3. Scope confusion: Build vs Runtime – Env vars in Netlify UI apply at *build time* (for Next.js, Hugo, etc.). Functions need *runtime* vars. These are two separate systems. Double-check you're not confusing process.env in build scripts with process.env in function handlers. See [environment variables in Netlify functions](/?guide=netlify-function-env) for comparison.

---

Debugging Checklist

  • [ ] Restart dev server: netlify dev --clear
  • [ ] Check function logs: Netlify Dashboard → Functions → view real-time logs
  • [ ] Verify netlify.toml has correct [[functions]] block (note double brackets)
  • [ ] Confirm vars are not secrets (secrets go in UI, but Functions need them in netlify.toml or passed explicitly)
  • [ ] Test locally: netlify dev loads env from .env file in project root
  • ---

    Official Resources

  • [Netlify Functions Environment Variables](https://docs.netlify.com/functions/overview/?fn-language=js#environment-variables)
  • [netlify.toml Functions Configuration](https://docs.netlify.com/configure-builds/file-api-reference/#functions)
  • ---

    Found a different variation? Drop it in the comments – If you hit a unique error or discovered another cause (Docker layer caching, monorepo-specific issues, region-based behavior), let us know and we'll add it to this guide.

    🔥 0d
    LIVE
    PlanetScale rage spiking Vercel pricing complaints Railway gaining fast Supabase happiness rising Resend loved by devs PlanetScale rage spiking Vercel pricing complaints Railway gaining fast Supabase happiness rising
    DEVELOPER PAIN RADAR // Loading...

    Developers complain.
    Opportunities appear.

    We track what developers are struggling with today — and what opportunities that creates.

    guides today
    avg happiness
    🔥 Pain
    📖 Guides
    🔭 Explore
    👤 Mine
    🔥 Pain Radar — rage scores today
    ↗ share
    💡 Opportunity Feed — pain = market gap
    📈 Tool Momentum
    all scores →
    📖 Latest Guide
    all guides →
    📖 All Guides
    📊 Tool Scores
    + Submit
    📰 Hacker News
    ➕ Submit a Tool
    ← back