# Diagnose Netlify serverless function failures: timeouts, 502s, and bundling errors

Use this when a request to `/.netlify/functions/[name]` fails. Classify from
the *client-facing symptom plus the function logs* (Site → Functions →
function name → logs) before changing code — timeouts, crashes, and bundling
problems look identical from the browser.

## 1. Read the logs; match one of these signatures

### A. `Task timed out after 10.02 seconds` → execution timeout

```
Feb 7, 07:06:00 PM: e85dcb5b 2023-02-08T03:06:10.894Z e85dcb5b-... Task timed out after 10.02 seconds
Feb 7, 07:06:10 PM: e85dcb5b Duration: 10016.68 ms   Memory Usage: 278 MB   Init Duration: 165.48 ms
```

The browser sees a **502**. Synchronous functions have a **10-second**
execution limit by default.

Fixes, in order of preference:

1. **Make the work fit.** Add `console.time` / `console.log` markers to find
   where the time goes. The usual culprits are slow upstream APIs and
   connections that keep the event loop alive — e.g. a database client that
   never closes makes the handler "finish in milliseconds" yet hang until the
   10-second timeout, exactly like the logs above.
2. **Raise the limit.** Netlify support can raise a site's (or account's)
   synchronous timeout to a maximum of **26 seconds** — this needs a Pro plan
   or above, and a new deploy for it to take effect.
3. **Move long work out of the request path.** Background functions run up to
   **15 minutes**: invoke them at `/.netlify/functions/[name]-background`; the
   caller gets a 202 immediately and the work runs asynchronously. Edge
   functions are the other escape hatch for streaming or long-lived workloads.

### B. 502 / "Function invocation failed" with NO timeout in the logs → the code crashed

The function threw before it could return. Read the exception in the logs —
but the Netlify-specific suspects are bundling problems:

1. **Runtime file reads fail** (`ENOENT` on a JSON/template/data file): the
   bundler only ships files it can statically trace. Declare extra files
   explicitly:

```toml
[functions]
  included_files = ["data/**"]
```

Globs support `!`-prefixed exclusions (e.g. `"!data/large/**"`).

2. **Native modules break under esbuild** (sharp, sqlite3, canvas, ...): they
   can't be inlined into the bundle. Ship them as-is in `node_modules`
   instead:

```toml
[functions]
  external_node_modules = ["sharp"]
```

3. **Bundle too big or bundler misbehaving:** switch the bundler explicitly
   and read the deploy log — it lists the largest files when a bundle is
   oversized. The hard ceiling is **250 MB unzipped per function**:

```toml
[functions]
  node_bundler = "esbuild"
```

(The bundler choices are `zisi`, `esbuild`, `esbuild_zisi`, and `nft`;
esbuild produces the smaller artifacts.)

### C. 404 at `/.netlify/functions/[name]` → the function was never deployed

Causes: the wrong functions directory (check `[functions] directory` or
`[build] functions` — both are relative to the base directory), the file
didn't match what the bundler expects, or a framework plugin wrote to a
different directory. Run `netlify build` locally and check which functions it
emits.

### D. Works locally, fails on Netlify

`netlify dev` serves functions with your local Node and your shell's
environment; production uses the configured bundler and the site's
environment variables. Two gotchas: env vars are injected at deploy time, so
a newly added variable needs a new deploy before the live function sees it;
and `netlify dev --context production` is the closest local approximation.

## 2. Config reference

```toml
[functions]
  directory = "netlify/functions"   # where your function sources live
  node_bundler = "esbuild"          # zisi | esbuild | esbuild_zisi | nft
  included_files = ["data/**"]      # extra files to ship with the bundle
  external_node_modules = ["sharp"] # modules shipped in node_modules, not inlined
```

## 3. Checklist

1. 502 + `Task timed out after 10.02 seconds` → fit the work under 10s, ask
   support for 26s (Pro+), or go background (`-background`, 202, 15 min).
2. 502 without a timeout → read the exception; then `included_files`,
   `external_node_modules`, `node_bundler`.
3. 404 → functions directory config (relative to base) and a local
   `netlify build` to see what's emitted.
4. Bundle deploy failures → esbuild plus pruned `included_files`; 250 MB
   unzipped is the ceiling.
5. New env var → new deploy; the live function won't see it otherwise.
