## TL;DR

For "Failed during stage "building site": Build script returned non-zero exit code: 2": the single most useful line in the deploy log is the stage marker, because each stage fails for different reasons: `` Failed during stage "building site": Build script returned non-zero exit code: 2 `` The base directory is where Netlify looks for package.json /.nvmrc, installs dependencies, and runs the build command. Fix: set base to the real subdirectory. Then recheck publish: it is resolved relative to the base directory, not the repo root.

```text
Failed during stage "building site": Build script returned non-zero exit code: 2
```

## When to use

You are seeing this: Open the deploy log and find the Failed during stage "..." line. - "building site", your build command (or its environment) failed. Use this skill when you run into "Diagnose and fix Netlify build failures: classify the failing stage first".

## When not to use

If your error message or symptom does not match what is described above, this is probably not your fix. Search for your exact error text instead of forcing this one to fit.

## Versions

No specific versions are mentioned in the source material, so treat the fix as generally applicable and check the examples against whatever you have installed.

Use this when a Netlify deploy fails during the build. The single most useful
line in the deploy log is the stage marker, because each stage fails for
different reasons:

```
Failed during stage "building site": Build script returned non-zero exit code: 2
```

## 1. Find which stage failed

Open the deploy log and find the `Failed during stage "..."` line.

- **`"building site"`**, your build command (or its environment) failed. Everything
 in section 2 applies.
- **`"deploying site"`**, the build succeeded but deploy-time processing failed
 (oversized function bundles, form/redirect processing). See the functions
 skill for bundle-size issues.
- **Earlier stages** (`"preparing repo"`, `"initializing"`), the problem is
 upstream of your code: a bad base directory, missing repo access, or invalid
 config. Read the lines immediately above the failure.

## 2. Classify by the error signature

Match the log's distinctive text to one of these before you touch config.

## A. "Base directory does not exist" or a missing publish directory

```
Base directory does not exist: /opt/build/repo/web
```

**Cause:** the base directory in build settings doesn't match a directory in the
repo. The base directory is where Netlify looks for `package.json` / `.nvmrc`,
installs dependencies, and runs the build command.

**Fix:** set base to the real subdirectory. Then recheck **publish**: it is
resolved *relative to the base directory*, not the repo root. The classic
double-up: base `web` plus publish `web/public` makes Netlify look for
`/opt/build/repo/web/web/public`. With base `web`, publish should be just
`public`.

## B. JavaScript heap out of memory, or "Killed"

```
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
```

```
10:14:05 AM: /opt/build-bin/build: line 77:  1351 Killed [... details about the build]
```

**Cause:** the build tried to allocate more memory than the build container has.
Gatsby builds hit this most often; anything bundling a huge dependency tree can
too. This is an infrastructure limit, not a bug in your code.

**Fix:** raise Node's old-space heap via a `NODE_OPTIONS` environment variable.
Per the Netlify support guide, `--max-old-space-size=4096` is a common value
that works, set it in site settings under Environment variables, or in
`netlify.toml`:

```toml
[build.environment]
  NODE_OPTIONS = "--max-old-space-size=4096"
```

Also: reduce parallel workers (parallel webpack/Gatsby workers multiply memory
contention without helping on a small build container), and if the site is
simply huge, Netlify's High-Performance Builds is the paid escape hatch.

## C. Your build command exited non-zero

```
Failed during stage "building site": Build script returned non-zero exit code: 2
```

**Cause:** your command failed. Scroll up for *your* tool's error, the stage
line is only the summary. Two Netlify-specific traps to check first:

1. **`CI` is set.** Netlify runs builds with `CI=true` (the log shows
 `$ CI= npm run build`). Some frameworks treat warnings as errors under CI 
 `react-scripts build` (create-react-app) is the classic case: warnings that
 pass locally fail the Netlify build. Either fix the warnings or override the
 behavior in build settings.
2. **Node version mismatch.** Engines incompatibility, or syntax errors on code
 that runs fine locally, means the build used the wrong Node. Pin it with a
 `NODE_VERSION` variable (or a `.nvmrc` file, or `package.json` engines):

```toml
[build.environment]
  NODE_VERSION = "20"
```

## D. Environment variable missing at build time

Symptoms: `ReferenceError: SOME_VAR is not defined`, or API calls returning
401/404 during the build.

**Causes, in order of likelihood:**

1. The variable was added or changed *after* the last deploy, env vars are
 baked in at build time, so you must trigger a new deploy.
2. Wrong deploy-context scoping: works on production but fails on deploy
 previews (or vice versa), check which contexts the variable is enabled for.
3. A `NETLIFY_`-prefixed name, that prefix is reserved and the UI rejects it
 (except `NETLIFY_AUTH_TOKEN` / `NETLIFY_SITE_ID`, which are CLI-local, not
 site variables).

## 3. The build config reference

The real keys in `netlify.toml`:

```toml
[build]
  command = "npm run build"        # build command
  publish = "dist"                 # output dir, relative to base
  base = ""                        # repo subdirectory to build from
  functions = "netlify/functions"  # serverless function sources
  edge_functions = "netlify/edge-functions"
  ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF -- src/"

[build.environment]
  NODE_VERSION = "20"

[context.deploy-preview]
  command = "npm run build:preview"  # per-context overrides
```

One rule that bites people: **settings in `netlify.toml` override the same
settings in the Netlify UI.** If a value lives in both places, the file wins 
keep it in one place.

To reproduce the build locally in the same environment, run `netlify build`
(Netlify CLI) from the repo root.

## 4. Checklist

1. Read the `Failed during stage` line first, only then scroll up for the real error.
2. Base/publish: `Base directory does not exist` → fix base; then fix publish
 (relative to base, not repo root).
3. OOM / `Killed` → `NODE_OPTIONS` with `--max-old-space-size=4096`; reduce parallelism.
4. Non-zero exit → find your tool's error; check the `CI=` warnings-as-errors
 trap; pin `NODE_VERSION`.
5. Missing env var → redeploy after adding it; check context scoping; don't use
 a `NETLIFY_` prefix.
6. `netlify.toml` overrides the UI, don't configure the same setting in both places.

## Variants

Other phrasings of the same problem that show up in reports:

### FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

```text
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
```

### 10:14:05 AM: /opt/build-bin/build: line 77: 1351 Killed [... details about the build]

```text
10:14:05 AM: /opt/build-bin/build: line 77:  1351 Killed [... details about the build]
```
