Fix ETXTBSY flake in vlt npx shim self-test - #441
Merged
Merged
Conversation
vlt_e2e_harness_npx_shim_forwards_the_args_after_the_package wrote
two shell scripts with fs::write and exec'd them right away. When a
sibling test thread forks while that write fd is open, its child keeps
the fd until it execs, and Linux refuses the exec with ETXTBSY ("Text
file busy"). This hit both exec sites: the test's own spawn of the
npx shim and the shim's `exec` of the fake socket binary. The latter
can't be fixed by retrying the spawn.
Add write_executable, which stages the bytes next to the target and
has a child `cp` create the executable, so the test process never
holds a writable fd to it. Use it for the npx/vlt shims (also exec'd
by the real-vlt legs) and the fake binary.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013p5RirmgQs47AecZUsbSmd
Collaborator
Author
|
bugbot run Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 30fdbaf. Configure here.
Collaborator
Author
|
[burn-down agent] Ready for review on
Generated by Claude Code |
Wenxin Jiang (Wenxin-Jiang)
approved these changes
Oct 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
vlt_e2e_harness_npx_shim_forwards_the_args_after_the_packagefails intermittently on Linux withText file busy(os error 26). It's a harness self-test invlt_e2e_common, so it is compiled into 7 test binaries (e2e_vlt,e2e_redirect_vlt_build,e2e_vendor_vlt_build,mode_migration_vlt,e2e_safety_vlt,e2e_hosted_production,e2e_vendored_production). It runs in everytest (ubuntu-latest),test-releaseandcoveragejob and in every vlte2eleg, which gives it many chances per PR to fail.Evidence:
test-releasefailed on attempt 1 (job 110175102290) and passed on a rerun of the same SHA18ff786b:CIruns this was the onlytest-releasefailure that wasn't either broken on the branch (all four test jobs red on one SHA) or limited to one PR's branch.Root cause
The test writes two shell scripts with
fs::write,chmods them, and execs them right away. Linux refusesexecveon a file that any process holds open for writing. If a sibling test thread forks while our write fd is open, its child inherits that fd and holds it until the child execs (the fd isO_CLOEXEC). Under load that window is long enough to overlap our exec.Locally I reproduced it at both exec sites:
Command::new(npx).output()→ExecutableFileBusyatmod.rs:2538(the CI failure)npxshim's ownexecof the fake socket binary →npx: 9: exec: …/socket: Text file busy, which surfaces as anassert_okfailure atmod.rs:777A retry around our spawn, like
exec_freshly_writteninupdate_fixture.rs, only covers the first site. The second exec happens insidesh, where we can't retry it.Fix
Add
write_executable(path, body)tovlt_e2e_common. On Unix it writes the bytes to<path>.staged, has a childcpcreatepath, removes the staged file, andchmod 755s the result. The test process never holds a writable fd to the executable, so no siblingfork()can inherit one. This closes the window itself instead of retrying. Windows keeps the plain write.write_executableis used for:socketscript in the self-testnpx/vltsh shims inwrite_shims_for. The real-vlt legs (write_shims) also exec these, so they had the same exposure.Coverage is unchanged: no test is removed, skipped or weakened, and every assertion stays the same.
Proof
Stress harness (a scratch test binary, not committed): it includes
vlt_e2e_commonand adds 8 sibling tests that spawn/bin/truein a tight loop for 1.5 s. Each iteration runs them alongside the shim test with--test-threads=16.Text file busy)Without the fork churn,
e2e_vltat--test-threads=32passed 200/200 even before the fix, which is consistent with a load-dependent race.Also run locally:
vlt_e2e_harness*self-tests: pass in all 7 binaries;e2e_vltnon-ignored suite: 24 passed.cargo clippy -p socket-patch-cli --test <each of the 7> -- -D warnings: no findings invlt_e2e_common. The only findings are pre-existing ones inprebuilt_common/mod.rs; that file is untouched, and CI's clippy job doesn't build test targets.rustfmt --checkon the changed file.--include-ignored vlt_pinned_matrix) here because the sandbox can't reach npmjs. The PR'se2e (*, *vlt*, …)CI jobs run them.Where tests run
Nothing is moved or removed. Every test runs where it did before.
🤖 Generated with Claude Code
https://claude.ai/code/session_013p5RirmgQs47AecZUsbSmd
Generated by Claude Code