Relationships
#2833 Concurrent embedded deno extraction races: stale temp-file cleanup deletes another process's in-flight .deno.tmp file (chmod ENOENT)
Opened by hammz · 9/30/2026· Shipped 9/30/2026
Summary
When two swamp processes extract the embedded deno binary at the same time, one can delete the other's in-flight temp file, so the victim fails with No such file or directory (os error 2): chmod '~/.swamp/deno/.deno.tmp.<uuid>'. Whatever command triggered the extraction (e.g. swamp extension pull) then fails.
Cause
EmbeddedDenoRuntime.extractEmbeddedDeno() (src/infrastructure/runtime/embedded_deno_runtime.ts) does:
cleanupStaleTempFiles(swampDir)— removes every.deno.tmp.*entry in~/.swamp/denoDeno.writeFile(tmpPath, …)to its own.deno.tmp.<uuid>Deno.chmod(tmpPath, 0o755)→Deno.rename(tmpPath, targetBinary)
Step 1 assumes every temp file is left over from a crashed extraction, but it cannot tell those apart from a temp file a concurrent live process is writing. Interleaving:
- Process A completes step 2
- Process B runs step 1 and deletes A's temp file
- A's
chmodfails with ENOENT
The in-process extractionPromise dedupes only within one process, not across processes. The cleanup was introduced in #2382 (swamp-club#2038, atomic-rename ETXTBSY fix).
Extraction only happens when ~/.swamp/deno/.version doesn't match the binary, so this is a cold-start race: first commands after installing/upgrading swamp, when more than one runs concurrently (parallel scripts, background jobs, multiple terminals, CI).
Observed
swamp-uat run https://github.com/swamp-club/swamp-uat/actions/runs/36761442017/job/110044726736 (binary 20260930.184839.0-sha.e47a5c51), tests/cli/extension/journeys/full_lifecycle_test.ts, run under deno test --parallel with a shared HOME:
Error: Install partially applied for @stack72/system-extensions — files extracted but bundling/importing the extension failed (No such file or directory (os error 2): chmod '/home/[REDACTED]/.swamp/deno/.deno.tmp.7a1fc3f8-79ab-4203-b8a6-331f16f0d814'). Run `swamp doctor extensions` to inspect, or retry `swamp extension pull @stack72/system-extensions` to reconcile.The next UAT run passed (the extraction had landed by then), so it presents as a flake.
Steps to reproduce
- Install a fresh compiled swamp binary (or delete
~/.swamp/deno/.version) - Start several commands that need deno at once, e.g.
swamp extension pull <ext>in several repos in parallel - Occasionally one fails with the
chmod … .deno.tmp.* … os error 2error above
Suggested fix
Only remove temp files that are clearly abandoned, e.g. mtime older than ~10 minutes, so crash leftovers still get cleaned without racing live extractions. The rest of the flow is already concurrency-safe (per-process UUID temp paths + atomic rename). Unit test: a fresh .deno.tmp.* survives cleanup, one aged with Deno.utime is removed.
This is a CLI bug, not a UAT bug — isolating SWAMP_HOME per UAT test would only mask it.
Environment
- swamp 20260930.184839.0-sha.e47a5c51 (compiled/standalone)
- Linux (GitHub Actions ubuntu runner)
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.