Process Completed With Exit Code 137: Fixes
Last updated
Process completed with exit code 137 means a process in your job was killed with SIGKILL, and on GitHub Actions the usual reason is that the runner ran out of memory. The number is 128 plus 9, where 9 is the SIGKILL signal. Below: how to confirm it is memory, how much RAM runners have, and fixes for Node, Jest, Gradle and Docker.
The exact message
Error: Process completed with exit code 137.
A shell step shows this when its command, or something it launched, died from SIGKILL. In Docker output you may also see Killed or OOMKilled: true. Other causes of 137 exist (a kill -9, a container stop timeout, a cancelled job), but out of memory is by far the most common in CI.
How much memory does a runner have?
Per the GitHub runner reference, checked October 2026:
| Repository | Standard Linux runner | RAM |
|---|---|---|
| Public | 4 CPU | 16 GB |
| Private | 2 CPU | 8 GB |
Many builds that pass on a 16 or 32 GB laptop fail on a private-repository runner with 8 GB, especially when a tool starts one worker per detected CPU core.
Confirm it is out of memory
- name: Memory check after failure
if: failure()
run: |
free -m
sudo dmesg | grep -i -E "killed process|out of memory" | tail
If dmesg shows Out of memory: Killed process, you have your answer. Also log memory every few seconds during the build:
- run: |
(while true; do date +%T; free -m | sed -n 2p; sleep 5; done) &
Causes and fixes
1. Node.js builds and bundlers
Node's default heap limit depends on the version and system memory, and large bundles can hit it. Raise the limit but keep it under the runner's RAM:
- run: npm run build
env:
NODE_OPTIONS: --max-old-space-size=6144
Also cut memory by turning off source maps in CI, or by running the type check and the build as separate steps.
2. Jest, Vitest or other test runners with too many workers
Each worker is a full process holding its own test environment, and leaky tests make it grow.
- run: npx jest --maxWorkers=2 --logHeapUsage
- run: npx jest --runInBand # slowest, lowest memory
Find leaks with --detectOpenHandles and --logHeapUsage. Jest 29 and later also support --workerIdleMemoryLimit=1GB, which restarts workers that grow too large. Test sharding spreads tests across jobs, which also splits memory use.
3. Gradle and Maven
The Gradle daemon, the Kotlin compile daemon and the test JVMs each take their own heap, and together they can exceed 8 GB.
- run: ./gradlew build --no-daemon
env:
GRADLE_OPTS: -Xmx2g
In gradle.properties:
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m
org.gradle.workers.max=2
kotlin.daemon.jvm.options=-Xmx1g
For Maven use MAVEN_OPTS: -Xmx2g and -T 1 instead of a large thread count. If tests fork JVMs, set forkCount and argLine with -Xmx in Surefire.
4. Docker builds and service containers
Either a container hit its memory limit or the host did. Compile steps inside docker build (Rust or C++ with high parallelism, for example) are typical. Lower parallelism (cargo build -j2, make -j2) and set explicit limits on memory-hungry services:
services:
es:
image: elasticsearch:8.15.0
env:
ES_JAVA_OPTS: -Xms512m -Xmx512m
5. A memory leak in your own code or tests
If memory rises with each test file, the cause is code. Fix the leak, or restart workers more often.
6. A cancelled or timed out job
Cancelling a run, or a step hitting [timeout-minutes](/errors/timeout-minutes), also kills processes. Rule out concurrency cancellation first (see The operation was canceled).
When tuning is not enough
Use a machine with more RAM. Options are a GitHub larger runner (see the larger runners docs) or a runner provider. compiler.dev's default Linux machine is 8 vCPU / 16 GB, a fresh VM per job, at $0.012 per minute as of October 2026, and you switch with runs-on: [self-hosted, Linux, compiler-ci]. See pricing and the runner size picker. If the job also runs out of disk, see No space left on device. A full runner crash looks different, see lost communication.
How to prevent it
- Record peak memory in CI and set an alert threshold below the runner's RAM.
- Pass explicit heap and worker limits to every tool instead of relying on auto-detection.
- Run linting, type checks, builds and tests as separate jobs.
- Run one heavy process at a time on small runners.
- Review dependency upgrades that increase bundle size or test memory.
FAQ
What does exit code 137 mean?
The process received SIGKILL. 137 equals 128 + 9. In CI it is almost always the kernel's out-of-memory killer.
Is exit code 137 the same as 143?
No. 143 is SIGTERM (128 + 15), a polite stop request often sent on cancellation. 137 is a forced kill.
Why does it work locally?
Your laptop has more RAM and a different core count. Runners have fixed, smaller memory.
Will adding swap help?
It can delay the kill but slows the build sharply. Reduce memory use or use a bigger machine instead.
Related
- API Rate Limit Exceeded in GitHub Actions: FixesFix "API rate limit exceeded for installation ID" in GitHub Actions: GITHUB_TOKEN allows 1,000 requests per hour per repo. Secondary limits and fixes.
- Cache Not Found for Input Keys: Causes and FixesCache not found for input keys in GitHub Actions is a cache miss, not always an error. Why it happens, how key and branch scope work, and fixes.
- Docker Pull Rate Limit in GitHub Actions: FixesFix "toomanyrequests: You have reached your pull rate limit" in GitHub Actions: Docker Hub limits, docker/login-action, mirrors and GHCR alternatives.
- Hosted Runner Lost Communication With the ServerFix "The hosted runner lost communication with the server" in GitHub Actions: causes like OOM, disk full and killed processes, with steps to diagnose.