Found while taking the machine-lane baseline for the QA wave (#9296, round #9298). Filed as a finding, not fixed — R0 lands no code.
Summary
pnpm test (i.e. turbo run test with no --concurrency) is killed by the kernel cgroup OOM killer on this container. It does not report a failing test — it dies. Reproduced twice; each run was killed at a different package, which is the signature of a resource kill rather than a package defect.
turbo.json declares no concurrency, so turbo defaults to 10 parallel tasks. This box is 4 CPU / ~15 GB. Ten concurrent test tasks, each forking its own vitest worker pool, exceeds the memory allowance.
Environment
- framework sha:
1f6d04703df8f518c80d0fbaa8b19262068e405d (tip of main at run time) .objectui-sha: 190fbd01d0615e2e168faf9e08b8ad7844bc039d- node v22.22.2, pnpm 10.31.0, linux x64,
nproc=4, 15 GB RAM - fresh
git worktree add --detach + a single pnpm install; deps prebuilt; run serialized under the shared verify lock (no other seat's gate running)
Reproduction
Run 1 — exit 137, 7m48s:
@objectstack/service-storage#test: ERROR command (…/packages/services/service-storage) pnpm run test exited (137)
Tasks: 75 successful, 91 total
Cached: 45 cached, 91 total
Time: 7m47.442s
Failed: @objectstack/service-storage#test
ERROR run failed: command exited (137)
Run 2 — exit 137, 6m57s, killed at a different package:
@objectstack/metadata-protocol#test: ERROR command (…/packages/metadata-protocol) pnpm run test exited (137)
Tasks: 75 cached, 91 total
Time: 6m57.19s
Failed: @objectstack/metadata-protocol#test
Run 1 carried NODE_OPTIONS=--max-old-space-size=4096; run 2 was run plain, with no NODE_OPTIONS at all, and died the same way. The heap flag is not the cause.
Kernel confirmation (dmesg), two events, one per run:
Memory cgroup out of memory: Killed process 28332 (node) total-vm:45803420kB, anon-rss:820896kB, …
No test actually failed
This is the part that matters for reading the gate. Across both runs, zero vitest summaries reported a failing test:
| run 1 | run 2 |
|---|
| vitest summaries emitted | 31 | 31 |
| tests passed | 8 914 | 8 914 |
| tests failed | 0 | 0 |
| turbo tasks successful | 75 / 91 | 75 / 91 |
The eight packages that logged ELIFECYCLE Test failed (objectql, spec, driver-sql, plugin-sharing, metadata-protocol, service-analytics, plugin-audit, service-storage in run 1) emitted no vitest failure summary — they were torn down by the kill, not reddened by an assertion. Read naively, the gate looks like eight broken packages; it is one OOM.
16 of 91 tasks never ran at all. A run that ends this way covers an unknown subset of the repo, so it cannot be reported as a pass or as a product failure.
What does work
Cutting the fan-out completes the suite:
pnpm exec turbo run test --continue --concurrency=2
Tasks: 135 successful, 136 total
Time: 30m38.998s
Peak memory during that run stayed around 7.6 GB used, versus the OOM at default fan-out.
Relationship to prior art
Same class as #9311 (closed, fixed) — a pnpm test at default concurrency that reds for reasons unrelated to the code under test — but a different mechanism: #9311 was two default-vitest-timeout cases losing a CPU race, and was correctly fixed by giving those cases real timeouts. This one is memory, not time, and no per-test timeout can address it. #9311 explicitly left the broader question open:
Separately worth a maintainer decision: whether turbo.json should pin a concurrency proportional to available CPUs, so the whole suite stops depending on runner size.
This issue is that question arriving with a measurement behind it. See also #4845 (closed) for the precedent that this repo's memory ceiling is a real constraint, not a theoretical one.
Suggested direction (not implemented here)
Decide whether pnpm test should be runnable on a small box at all, and if so how:
- pin
concurrency in turbo.json (or in the test script) proportional to nproc, so the suite stops depending on runner size; or - leave the default and document
--concurrency=2 as the supported invocation on constrained runners, so agents and contributors stop reading an OOM as eight broken packages.
The first option is a repo-wide behaviour change and is a maintainer call, which is why nothing is changed here.
Generated by Claude Code
Found while taking the machine-lane baseline for the QA wave (#9296, round #9298). Filed as a finding, not fixed — R0 lands no code.
Summary
pnpm test(i.e.turbo run testwith no--concurrency) is killed by the kernel cgroup OOM killer on this container. It does not report a failing test — it dies. Reproduced twice; each run was killed at a different package, which is the signature of a resource kill rather than a package defect.turbo.jsondeclares noconcurrency, so turbo defaults to 10 parallel tasks. This box is 4 CPU / ~15 GB. Ten concurrenttesttasks, each forking its own vitest worker pool, exceeds the memory allowance.Environment
1f6d04703df8f518c80d0fbaa8b19262068e405d(tip ofmainat run time).objectui-sha:190fbd01d0615e2e168faf9e08b8ad7844bc039dnproc=4, 15 GB RAMgit worktree add --detach+ a singlepnpm install; deps prebuilt; run serialized under the shared verify lock (no other seat's gate running)Reproduction
pnpm testRun 1 — exit 137, 7m48s:
Run 2 — exit 137, 6m57s, killed at a different package:
Run 1 carried
NODE_OPTIONS=--max-old-space-size=4096; run 2 was run plain, with noNODE_OPTIONSat all, and died the same way. The heap flag is not the cause.Kernel confirmation (
dmesg), two events, one per run:No test actually failed
This is the part that matters for reading the gate. Across both runs, zero vitest summaries reported a failing test:
The eight packages that logged
ELIFECYCLE Test failed(objectql,spec,driver-sql,plugin-sharing,metadata-protocol,service-analytics,plugin-audit,service-storagein run 1) emitted no vitest failure summary — they were torn down by the kill, not reddened by an assertion. Read naively, the gate looks like eight broken packages; it is one OOM.16 of 91 tasks never ran at all. A run that ends this way covers an unknown subset of the repo, so it cannot be reported as a pass or as a product failure.
What does work
Cutting the fan-out completes the suite:
Peak memory during that run stayed around 7.6 GB used, versus the OOM at default fan-out.
Relationship to prior art
Same class as #9311 (closed, fixed) — a
pnpm testat default concurrency that reds for reasons unrelated to the code under test — but a different mechanism: #9311 was two default-vitest-timeout cases losing a CPU race, and was correctly fixed by giving those cases real timeouts. This one is memory, not time, and no per-test timeout can address it. #9311 explicitly left the broader question open:This issue is that question arriving with a measurement behind it. See also #4845 (closed) for the precedent that this repo's memory ceiling is a real constraint, not a theoretical one.
Suggested direction (not implemented here)
Decide whether
pnpm testshould be runnable on a small box at all, and if so how:concurrencyinturbo.json(or in thetestscript) proportional tonproc, so the suite stops depending on runner size; or--concurrency=2as the supported invocation on constrained runners, so agents and contributors stop reading an OOM as eight broken packages.The first option is a repo-wide behaviour change and is a maintainer call, which is why nothing is changed here.
Generated by Claude Code