Netlify: env vars not loading in functions [2026 fix]
Netlify functions can't access env vars because they're not deployed in netlify.toml or using legacy config. Deploy with netlify.toml [functions] section.
Netlify: Environment Variables Not Loading in Functions
TL;DR
Cause: Your environment variables are set in Netlify UI but not declared in netlify.toml under [functions], or you're using legacy function deployment without proper config.
Fix: Add environment = { VAR_NAME = "value" } to your netlify.toml [functions] section and redeploy—UI-only vars don't auto-inject into serverless functions.
---
Real Console Error Messages
Here are the exact errors you'll see in your 2am panic:
``` Uncaught ReferenceError: process.env.DATABASE_URL is not defined at handler (/var/task/functions/api.js:5:3) ```
``` Error: STRIPE_SECRET_KEY is undefined. Check your environment configuration. at Object.<anonymous> (functions/payments.js:1) ```
``` TypeError: Cannot read property 'split' of undefined at getApiKey (functions/auth.js:12:3) [process.env.API_KEY is undefined] ```
``` Netlify Functions runtime error: Missing required environment variable MESSAGE: Cannot find API_URL in function scope ```
``` FunctionError: Your function returned an error: TypeError: database_host is undefined CAUSED BY: process.env.database_host was not injected at runtime ```
---
Broken Code vs. Fix
❌ BROKEN: Environment vars in UI only
```javascript // netlify.toml - INCOMPLETE [build] command = "npm run build" functions = "netlify/functions"
// functions/api.js exports.handler = async (event) => { const dbUrl = process.env.DATABASE_URL; // ❌ UNDEFINED - only set in UI const stripe = require('stripe')(process.env.STRIPE_KEY); // ❌ NULL return { statusCode: 500, body: 'Vars not loaded' }; }; ```
✅ FIXED: Declare vars in netlify.toml
```toml
netlify.toml - CORRECT
[build] command = "npm run build" functions = "netlify/functions"[functions] environment = { DATABASE_URL = "postgresql://user:pass@host/db", STRIPE_KEY = "sk_live_abc123", LOG_LEVEL = "debug" } ```
```javascript // functions/api.js - NOW WORKS exports.handler = async (event) => { const dbUrl = process.env.DATABASE_URL; // ✅ "postgresql://user:pass@host/db" const stripe = require('stripe')(process.env.STRIPE_KEY); // ✅ "sk_live_abc123" return { statusCode: 200, body: JSON.stringify({ status: 'connected' }) }; }; ```
---
Why This Happens
Netlify UI environment variables are available to build processes and site context but not automatically injected into serverless function runtime unless you:
1. Define them in netlify.toml [functions] block (recommended for production)
2. Use Netlify's environment variable injection API (for dynamic values)
3. Set them per-function with individual config files (for granular control)
This is a security boundary: functions get explicit variable allowlists to prevent accidental secret leakage.
---
Step-by-Step Fix
Step 1: Open your netlify.toml at project root
Step 2: Add or update the [functions] section:
```toml [functions] environment = { DATABASE_URL = "your_actual_value", API_KEY = "sk_test_xyz" } ```
Step 3: For sensitive secrets, use Netlify's UI Settings > Build & Deploy > Environment variables, then reference them in netlify.toml:
```toml
Set these in UI, they'll be available as $VAR_NAME in netlify.toml
[functions] environment = { SECRET = "$SECRET_FROM_UI" } ```Step 4: Commit and push:
```bash git add netlify.toml git commit -m "fix: add env vars to functions config" git push origin main ```
Step 5: Netlify redeploys automatically—check Deployments tab for completion (2-5 min)
Step 6: Test your function:
```bash curl https://your-site.netlify.app/.netlify/functions/api
Should return 200, not undefined errors
```---
Still Broken? Check These Too
1. Function naming mismatch: File is api.js but you're calling .netlify/functions/api-handler? Netlify function routes match filenames exactly (minus .js). Rename files to match your routes. See [Netlify function routing guide](/?guide=netlify-function-routing).
2. Build-time vs. runtime confusion: Variables used *during build* (Next.js, Gatsby) need NEXT_PUBLIC_* prefix for client exposure. Backend function vars use plain PROCESS_ENV. Don't mix them. Check [environment variable scoping](/?guide=env-var-scope).
3. Stale function cache: Netlify caches old function builds. Clear it by navigating to Deploys > trigger deploy or delete .netlify folder and rebuild locally with netlify build && netlify deploy.
---
Official Docs
[Netlify Functions Environment Variables Documentation](https://docs.netlify.com/functions/overview/#environment-variables)
[netlify.toml Reference](https://docs.netlify.com/configure-builds/file-api-reference/)
---
Version Notes
This fix applies to Netlify Functions on any runtime (Node 18+, Python, Go). Behavior is consistent as of 2026. If you're using Netlify Edge Functions (edge compute), variables are accessed the same way but deployed with different syntax—[check Edge Functions docs](https://docs.netlify.com/edge-functions/overview/) if .netlify/functions isn't your deployment target.
---
Found a different variation? Drop it in the comments—did you hit this with a specific framework (Next.js, Remix, SvelteKit) or a different error message? Let us know so we can expand this guide.