Diagnosing Netlify Functions Deployment and Invocation Failures
A diagnostic guide for Netlify Node.js Functions that fail to deploy or return 502 errors, covering handler exports, timeouts, environment variables, CORS headers, and bundle size limits.
10 Dec 2025, 03:05 UTC

The problem and what to check first
You push a Netlify site, the build completes, but calling /.netlify/functions/ returns a 502 Bad Gateway—or the build fails and the function never appears in the dashboard. The useful takeaway is that most Netlify Node.js Function failures come from a short list of packaging, timeout, environment-variable, and response-header issues. You can reproduce nearly all of them locally with the Netlify CLI before you push again.
This guide covers Netlify Node.js Functions, which are packaged from a functions directory and deployed as Lambda-compatible endpoints. It does not cover Edge Functions, which use a different runtime and have different limits.
Recognizable conditions
- The build fails with a message that a function handler is missing or cannot be found.
- The function deploys but returns HTTP 502 or times out when invoked.
- The function works with
netlify devbut fails in production. - A browser call from the frontend is blocked by a CORS error.
- The deployment is rejected because the function bundle is too large.
Cause and diagnostic table
| Symptom | Likely cause | First check |
|---|---|---|
| Build fails; function absent from dashboard | Missing handler export or incorrect file name | Open functions/.js or .ts and confirm it exports handler |
| Invocation returns 502 or timeout | Handler threw an unhandled error or exceeded the function timeout | Open the Functions tab in the Netlify dashboard and read the invocation log |
| Works locally, fails in production | Environment variable set in UI but missing locally, or vice versa | Compare Netlify site environment variables with your local .env |
| Browser receives CORS error | Response lacks Access-Control-Allow-Origin | Run curl -I against the function URL and inspect headers |
| Upload rejected before function is created | Deployment payload exceeds the function bundle size limit | Check the total size of the function directory |
Ordered checks
1. Reproduce the failure locally
Run the Netlify CLI in your site root so the local proxy routes /.netlify/functions/ to your function code.
# Run in the site root. No special permissions required.
netlify dev
Expected check: open http://localhost:8888/.netlify/functions/ in a browser or with curl. If the local call fails with the same error, the issue is in your function code or configuration, not the Netlify platform.
Risk: a local .env file can supply variables that are missing in production, making the function appear healthy locally when it will fail after deployment.
2. Confirm the handler export
Netlify looks for a function file that exports a handler function. A missing or incorrectly named export prevents deployment.
// functions/hello.js
export const handler = async (event, context) => {
return {
statusCode: 200,
body: JSON.stringify({ message: "ok" }),
};
};
Expected check: the file exports handler as a named export (ES modules) or module.exports.handler (CommonJS), depending on your setup. If the file exports something else, rename the export or the file.
3. Verify the functions directory in netlify.toml
If your function files live outside the default functions folder, Netlify needs to know where to package them from.
# netlify.toml at the site root
[build]
functions = "functions"
Expected check: confirm the path matches the folder that contains your function files. If you recently moved functions, update this value and redeploy.
4. Check dependencies in the functions folder
Build failures often happen when a function imports a package that is not installed. Dependencies for functions are resolved from the functions directory or the site root, depending on your configuration.
Expected check: if functions/hello.js imports axios, confirm axios is installed and available to the function bundle. A missing dependency usually appears in the build log as a module-not-found error.
5. Inspect timeout and blocking work
A 502 or timeout can mean the handler exceeded the function timeout limit or performed unsupported blocking I/O in the Node.js runtime. Limits vary by plan, so verify the current quota in Netlify documentation before designing around a specific number.
Expected check: in the dashboard Functions tab, find the invocation and look for "timeout" or "task timed out" in the log. If present, shorten the handler, move long-running work to a background function, or request a plan review if your workload genuinely needs more time.
6. Compare environment variables between local and production
Environment variables set in the Netlify UI are not available during local development with Netlify CLI unless they are mirrored in a .env file.
# .env at the site root (local development only)
API_KEY=local-development-value
Expected check: if the function reads process.env.API_KEY and works locally but returns 502 in production, confirm the variable exists in Site settings > Build & deploy > Environment. Never copy production secrets into .env; use local-only placeholder values.
Risk: committing .env with real secrets exposes them in version control. Add .env to .gitignore.
7. Test CORS headers with curl
CORS errors in the browser usually mean the function response omits Access-Control-Allow-Origin.
# Run in your terminal. Replace the placeholder with your site URL and function name.
curl -I https://.netlify.app/.netlify/functions/
Expected check: the response headers should include access-control-allow-origin. If the header is missing, add it to the function response:
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "https://your-frontend-domain.com",
},
body: JSON.stringify({ message: "ok" }),
};
8. Check the function bundle size
Large deployment payloads above the function bundle size limit cause upload rejection before the function is created. The limit varies by plan.
Expected check: review the deploy log for a bundle-size rejection. If found, remove unnecessary files from the functions directory, avoid bundling large client-side libraries, or move heavy dependencies to a layer where supported.
Fixes tied to findings
- Missing handler: rename the export to
handleror rename the file so Netlify detects it. - Wrong functions path: add or correct
[build] functions = "functions"innetlify.toml. - Timeout: optimize the handler, split long tasks, or verify your plan’s timeout allowance.
- Missing production env var: add it in the Netlify UI; keep
.envfor local placeholders only. - CORS: return
Access-Control-Allow-Originfrom the handler. - Bundle too large: prune the function directory and redeploy.
Escalation criteria
Contact Netlify support or reconsider your architecture when:
- The dashboard log shows an internal platform error with no issue in your function code.
- Your workload requires a timeout longer than your plan allows.
- The function bundle cannot be reduced below the limit but requires large dependencies.
- You need edge-specific behavior such as geographic request routing; in that case, evaluate Netlify Edge Functions separately, since they are not Node.js Functions.
Limitations and practical verification
Timeout and bundle size limits vary by Netlify plan and may change; verify current quotas in Netlify documentation before designing around specific numbers. This guide does not cover Edge Functions, which have a different runtime and limits.
Practical verification: after making a fix, run netlify dev, call /.netlify/functions/ locally, then push and confirm the same URL returns 200 in production. Check the Functions tab in the dashboard for the invocation log.
Rollback: editing netlify.toml or .env changes repository state. To roll back, revert the file to its previous committed version and redeploy the site.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.