You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Hackathon] fix(bundle): re-fetching a failure bundle into an existing --out fails when its parent isn't writable or --out is a mount point (EACCES/EXDEV) #640
Fetching a failure bundle a second time into the same --out directory fails whenever the commit needs to rotate the previous artifacts: commitBundle stages old files into an aside directory it creates in dirname(--out) (join(parent, '.<base>.aside.<uuid>'), src/lib/bundle.ts ~line 630). Two real setups break:
--out is writable but its parent is not — e.g. --out=/workspace/out under a root-owned /workspace, or a shared top-level dir — so mkdir of the aside fails with EACCES.
--out is itself a mount point (Docker/CI volume, bind mount, tmpfs) — so rename() of the old artifacts into the aside crosses devices and fails with EXDEV.
The first fetch succeeds; every later fetch into that dir fails and leaves a .partial marker behind, so consumers treat the bundle as incomplete. The only workaround is deleting --out by hand.
consts=findSample('GET','https://api.testsprite.com/api/cli/v1/tests/test_8f2a4d10/failure');constctx=s.body().data;awaitwriteBundle(ctx,{dir, fetchImpl});// OK — 12 fileschmodSync(parent(dir),0o555);awaitwriteBundle(ctx,{dir, fetchImpl});// EACCES: mkdir '<parent>/.out.aside.<uuid>'// dir afterwards: .partial code.py failure.json meta.json result.json steps video.mp4
Mount-point case (tmpfs): second run fails EXDEV: cross-device link not permitted, rename '<dir>/meta.json' -> '<parent>/.mnt.aside.<uuid>/meta.json', same .partial + stale contents.
Expected: any --out the first fetch wrote successfully can be refreshed in place — stage the aside inside --out (same filesystem, known-writable) or fall back to an in-dir aside / copy+unlink on EACCES/EXDEV.
Details / implementation notes
Root cause: commitBundle (src/lib/bundle.ts ~line 630) builds the aside at join(dirname(dir), \..aside.`)andrename()s the managed files into it. That crosses a permission boundary (parent can be read-only while dir itself is writable) and a device boundary (dir` can be a mount point while the parent lives on another filesystem).
Fix: create the aside inside dir (e.g. dir/.aside.<uuid>, excluded from the managed/stale-entry sweep) so every rename stays on one filesystem in a directory known to be writable — the same place the tmp staging already lives. Fallback alternative: keep the current location but retry with an in-dir aside (or copy+unlink) when mkdir/rename returns EACCES/EXDEV. Regression test: write once, chmod 0555 the parent, write again — must succeed and leave a complete bundle with no .partial marker.
A PR with the fix + regression test will follow referencing this issue.
Confirmations
I have searched existing issues and this is not a duplicate.
I have provided my Discord identity above for reward coordination.
Thanks @Satianurag. It needs a repeat fetch into the same --out whose parent is read-only or which is itself a mount point, and a fresh or emptied directory works; no one has hit it in practice. Closing as not planned.
Activity
fyZhang66 commentedon Oct 22, 2026
Thanks @Satianurag. It needs a repeat fetch into the same
--outwhose parent is read-only or which is itself a mount point, and a fresh or emptied directory works; no one has hit it in practice. Closing as not planned.