Empty a repo clone when Windows will not delete its root - #58
Merged
Conversation
Third attempt at this, and the first one grounded in a measurement rather than a
theory. Two 2-task runs with the failure path instrumented:
could not remove clone ...swe-repo-django_django-EDY54e: EPERM
remains: 28 entr(ies): .editorconfig, .eslintignore, .eslintrc, .git, ...
probe: removing inner '.editorconfig' SUCCEEDED (file level is free)
probe: rmdir on the clone root FAILED: EBUSY: resource busy or locked
Two facts settle it. The tree is INTACT -- 28 entries, .git included -- so
fs.rmSync(recursive) aborts on its first operation and cleans nothing, rather
than clearing the tree and failing on a final rmdir. And the root is EBUSY while
its contents delete on demand, so the lock is on the DIRECTORY, not the files.
No child process survives, and the root frees the instant the test process
exits, which puts the handle inside this process.
I did not find what holds it, and I stopped looking. The two previous fixes were
root-cause guesses and both did nothing: a longer retry budget (bc429f3) on the
theory that cleanup raced the shell's 3s force-kill, then a whole-process-tree
kill (97fe8ab) on the theory that a grandchild held it. A third guess is worth
less than working with the behaviour actually measured.
So: if the root will not go, take everything inside it. Verified -- the leftover
directory now has 0 entries instead of 28, turning a ~35MB leak per repo per run
into a directory entry that sweepStaleClones() collects later.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
✅ All checks passed
Posted by SideCarAI-Bot |
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.
Third attempt at the SWE-harness clone leak, and the first grounded in a measurement rather than a theory.
What the evidence shows
Instrumenting the failure path over two 2-task runs:
Two facts settle it:
.gitincluded.fs.rmSync(recursive)aborts on its first operation and cleans nothing, rather than clearing the tree and failing on a finalrmdir. I had previously assumed the latter, and that wrong assumption is what made "a directory handle is held" look like a complete explanation.No child process survives, and the root frees the instant the test process exits, which puts the handle inside the test process.
What this does not do
It does not identify what holds the handle. I stopped looking deliberately. The two previous fixes were root-cause guesses and both changed nothing:
ShellSession.dispose()'s 3s force-killA third guess is worth less than working with the behaviour actually measured.
What it does
If the root will not go, take everything inside it. Verified end-to-end: the leftover directory now has 0 entries instead of 28, turning a ~35MB-per-repo-per-run leak into a directory entry that
sweepStaleClones()collects on a later run.For scale: 167 clones and several GB had accumulated over two days of measurement runs before anyone looked at the temp folder.
🤖 Generated with Claude Code