CircleCI Build Failed: How to Fix OOM, Permission Denied, and Timeout Errors
CircleCI build failed? Diagnose and fix out-of-memory, permission denied, and timeout errors with step-by-step commands and config fixes. Resolve in minutes.
- CircleCI builds fail for four primary reasons: insufficient executor memory (OOM kill), filesystem or registry permission errors, step-level or job-level timeouts, and misconfigured environment variables or orb versions.
- Out-of-memory failures surface as 'Killed' or exit code 137 in logs; permission errors appear as 'EACCES', 'permission denied', or 'exit status 126'; timeouts show 'Exceeded 10 hours (with no output)' or 'CircleCI received exit code 124'.
- Quick triage: check the failing step's raw log for the exact exit code, then cross-reference with the resource_class, working_directory, and environment block in your .circleci/config.yml before reaching for SSH debugging.
| Method | When to Use | Time to Apply | Risk |
|---|---|---|---|
| Upgrade resource_class | Exit code 137 / OOM kill / heap exhaustion | 5 min (config change) | Low — minor cost increase |
| SSH debug session | Intermittent failures, undocumented permission errors | 15–30 min | Low — read-only by default |
| Restore cache with a new key | Corrupt node_modules or pip cache causing permission errors | 10 min | Low — forces clean install |
| Increase no_output_timeout | Long compile or test steps that time out silently | 2 min (config change) | Low — extends billing |
| Rerun with --debug or verbose flags | Opaque failures with no clear error message | 5 min | Low — log verbosity only |
| Rerun failed jobs from the start | Flaky network-dependent steps (Docker pull, npm install) | 1 min (UI click) | None |
| Downgrade or pin orb version | Orb update introduced breaking changes | 10 min | Medium — may miss security patches |
Understanding Why CircleCI Builds Fail
CircleCI executes your pipeline inside an ephemeral executor — a Docker container, Linux VM, macOS VM, or Windows VM — that is created fresh for every job. Because the environment is isolated and stateless, failures that "never happen locally" are almost always caused by one of four environmental mismatches: resource limits, filesystem permissions, wall-clock timeouts, or missing credentials.
Understanding the exit code in the failing step is the fastest path to the correct fix.
| Exit Code | Signal | Most Likely Cause |
|---|---|---|
| 1 | General error | Script logic, non-zero test result |
| 124 | SIGALRM | timeout command or CircleCI step timeout |
| 126 | Permission error | Binary not executable |
| 127 | Not found | Missing dependency or PATH issue |
| 137 | SIGKILL (OOM) | Executor ran out of memory |
| 139 | SIGSEGV | Segmentation fault in native code |
Step 1: Read the Raw Log
Click the failing step name in the CircleCI UI to expand it. Scroll to the very bottom of that step — CircleCI truncates output in the middle but always preserves the final lines where the fatal error appears. Look for:
Killed
Exited with code exit status 137
or
permission denied: ./scripts/deploy.sh
Exited with code exit status 126
or
Too long with no output (exceeded 10m0s): context deadline exceeded
If the log is ambiguous, enable SSH access: open the job in the UI, click Rerun job with SSH, wait for it to start, then copy the SSH command shown and connect. From there you can ls -la, inspect /proc/meminfo, or re-run the failing command interactively.
Step 2: Fix Out-of-Memory (OOM) Failures — Exit Code 137
The Linux OOM killer sends SIGKILL (signal 9) to the process consuming the most memory, which is why you see Killed with no further explanation. Jest, webpack, Gradle, and the JVM are the most common culprits.
Fix A — Upgrade the resource_class:
jobs:
build:
docker:
- image: cimg/node:20.11
resource_class: medium+ # was: medium (2 vCPU / 4 GB)
steps:
- checkout
- run: npm ci
- run: npm test
Available Docker resource classes in ascending memory order: small (1 vCPU / 2 GB), medium (2/4 GB), medium+ (3/6 GB), large (4/8 GB), xlarge (8/16 GB), 2xlarge (16/32 GB).
Fix B — Limit Node.js heap size:
- run:
name: Run Jest with bounded heap
command: node --max-old-space-size=2048 node_modules/.bin/jest --runInBand
environment:
NODE_OPTIONS: "--max-old-space-size=2048"
The --runInBand flag forces Jest to run tests serially rather than forking worker processes, dramatically reducing peak memory.
Fix C — Limit JVM heap for Gradle/Maven:
- run:
name: Build with bounded JVM
command: ./gradlew build
environment:
JAVA_TOOL_OPTIONS: "-Xmx2g -Xms512m"
Step 3: Fix Permission Denied Errors — Exit Code 126 / EACCES
Permission errors in CircleCI fall into three sub-categories:
3a. Script not executable
If your repo contains shell scripts committed without the executable bit, CircleCI will fail with permission denied. Fix it locally and recommit:
git update-index --chmod=+x scripts/deploy.sh
git commit -m "fix: make deploy script executable"
git push
Alternatively, call bash explicitly inside your config so the executable bit is irrelevant:
- run: bash scripts/deploy.sh
3b. Docker socket or volume permission errors
When running Docker-in-Docker, the circleci user inside the executor may not have access to /var/run/docker.sock. Use the setup_remote_docker step and never mount the host socket directly:
steps:
- setup_remote_docker:
version: docker24
docker_layer_caching: true
- run: docker build -t myapp:$CIRCLE_SHA1 .
3c. npm / pip global install permission errors
Global installs attempt to write to /usr/local/lib, which requires root. Instead, configure a per-user prefix:
- run:
name: Configure npm global prefix
command: |
mkdir -p ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> $BASH_ENV
- run: npm install -g your-tool
For pip, prefer pip install --user or use a virtualenv:
- run: pip install --user -r requirements.txt
Step 4: Fix Timeout Errors
CircleCI enforces two independent timeout mechanisms:
- No-output timeout (default: 10 minutes) — if a step produces no stdout/stderr for 10 minutes, CircleCI kills it.
- Job-level timeout — free tier jobs are killed after 30 minutes; paid tiers allow up to 5 hours by default.
Fix A — Increase no_output_timeout for long steps:
- run:
name: Run integration tests
command: pytest tests/integration/ -v
no_output_timeout: 30m
Fix B — Force progress output for silent steps (e.g., large downloads):
- run:
name: Download large artifact with progress
command: curl -# -o artifact.tar.gz https://storage.example.com/artifact.tar.gz
The -# flag outputs a progress bar, which counts as output and resets the no-output timer.
Fix C — Increase job timeout at the workflow level:
workflows:
build_and_deploy:
jobs:
- build:
context: production
- deploy:
requires:
- build
Note: per-job wall-clock timeouts require a CircleCI paid plan and are set in the CircleCI project settings UI under Advanced Settings > Maximum Job Runtime.
Step 5: Validate Your Config Before Pushing
Many build failures are caused by YAML syntax errors or invalid config keys that could be caught locally:
# Install the CircleCI CLI
curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | bash
# Validate config
circleci config validate .circleci/config.yml
# Pack and validate if using orbs or multiple files
circleci config pack .circleci/ | circleci config validate -
# Process config to see the expanded version
circleci config process .circleci/config.yml
Step 6: Isolate Flaky Failures with Rerun
If a build fails inconsistently (especially on Docker image pulls or npm install), use CircleCI's rerun capabilities:
- Rerun from failed: restarts only the failed job, preserving workspace artifacts from earlier jobs.
- Rerun with SSH: provides an interactive shell for manual investigation.
- Rerun with no cache: forces a clean build, bypassing potentially corrupt caches.
To trigger a no-cache rerun via the API:
curl -X POST \
--url "https://circleci.com/api/v2/pipeline" \
--header "Circle-Token: $CIRCLECI_TOKEN" \
--header "content-type: application/json" \
--data '{"branch": "main", "parameters": {"run_without_cache": true}}'
Frequently Asked Questions
#!/usr/bin/env bash
# CircleCI Build Failure Diagnostic Script
# Run these commands locally (requires circleci CLI) or in an SSH debug session
set -euo pipefail
echo "=== 1. Validate CircleCI config ==="
circleci config validate .circleci/config.yml && echo "Config is valid" || echo "Config has errors"
echo ""
echo "=== 2. Check executable bits on all scripts referenced in config ==="
grep -Eo '(bash|sh|run:.*\./[^ ]+)' .circleci/config.yml \
| grep -Eo '\./[^ ]+' \
| sort -u \
| while read -r script; do
if [ -f "$script" ]; then
if [ ! -x "$script" ]; then
echo "NOT EXECUTABLE: $script (fix: git update-index --chmod=+x $script)"
else
echo "OK: $script"
fi
fi
done
echo ""
echo "=== 3. List resource_class settings in config ==="
grep -A1 'resource_class' .circleci/config.yml || echo "No explicit resource_class found (defaulting to medium)"
echo ""
echo "=== 4. Check for no_output_timeout settings ==="
grep -n 'no_output_timeout' .circleci/config.yml || echo "No custom timeouts found (default is 10m)"
echo ""
echo "=== 5. Detect potential OOM risk — Node.js heap config ==="
if grep -q 'jest\|webpack\|next build' .circleci/config.yml; then
if ! grep -q 'max-old-space-size\|NODE_OPTIONS' .circleci/config.yml; then
echo "WARNING: Node.js build steps found but no heap limit set. Add NODE_OPTIONS=--max-old-space-size=2048"
else
echo "OK: NODE_OPTIONS heap limit is configured"
fi
fi
echo ""
echo "=== 6. Detect potential OOM risk — JVM config ==="
if grep -q 'gradlew\|mvn\|java' .circleci/config.yml; then
if ! grep -q 'JAVA_TOOL_OPTIONS\|Xmx' .circleci/config.yml; then
echo "WARNING: JVM steps found but no heap limit. Add JAVA_TOOL_OPTIONS=-Xmx2g"
else
echo "OK: JVM heap limit is configured"
fi
fi
echo ""
echo "=== 7. Locally simulate the job (requires Docker) ==="
echo "Run this command to execute your build job locally:"
echo " circleci local execute --job build"
echo ""
echo "=== 8. Process and expand full config (shows orb substitutions) ==="
circleci config process .circleci/config.yml > /tmp/circleci_expanded.yml
echo "Expanded config written to /tmp/circleci_expanded.yml"
wc -l /tmp/circleci_expanded.yml
echo ""
echo "=== Diagnostic complete ==="Error Medic Editorial
Error Medic Editorial is a team of senior DevOps and SRE engineers who have collectively operated CI/CD pipelines across hundreds of production environments. We specialize in distilling complex infrastructure failure patterns into actionable, evidence-based troubleshooting guides. Our contributors have deep experience with CircleCI, GitHub Actions, Jenkins, and Kubernetes, and we verify every command in a live environment before publishing.
Sources
- https://circleci.com/docs/troubleshooting/
- https://circleci.com/docs/configuration-reference/#resourceclass
- https://circleci.com/docs/ssh-access-jobs/
- https://discuss.circleci.com/t/builds-timing-out-due-to-no-output/1005
- https://circleci.com/docs/local-cli/
- https://stackoverflow.com/questions/53230823/circleci-exit-code-137-killed