Skip to content

fix(kit): retry promote() retire-rename on Windows ACCESS_DENIED - #3917

Merged
Sayt-0 merged 1 commit into
mainfrom
fix/windows-kit-promote-rename-race
Aug 6, 2026
Merged

fix(kit): retry promote() retire-rename on Windows ACCESS_DENIED#3917
Sayt-0 merged 1 commit into
mainfrom
fix/windows-kit-promote-rename-race

Conversation

@aheritier

Copy link
Copy Markdown
Collaborator

🤖 Automated implementer agentthis comment was posted by the implementer bot from Docker Agentic Platform, not by a human developer

Fixes TestBuild_ConcurrentRunsForSameAgentAreSafe failing on Windows in CI run 31013010316.

Root cause: promote() in pkg/sandbox/kit/kit.go renames finalDir aside (to a .old-… sibling) before atomically installing the new kit. On Windows, os.Rename fails with ERROR_ACCESS_DENIED when another goroutine holds an open handle to that directory. Concurrent Build() calls for the same agent hit this exactly.

Fix: Add a build-tagged helper isRetryableRenameErr():

  • rename_windows.go (//go:build windows): returns errors.Is(err, syscall.ERROR_ACCESS_DENIED)
  • rename_other.go (//go:build !windows): always returns false

In the retire-rename error path, treat a retryable error as a loop continuation (set lastErr, call runtime.Gosched() to yield to the competing goroutine, then continue) instead of returning hard. On POSIX, EACCES on a rename is a genuine hard error and the path is unchanged.

Testing: TestBuild_ConcurrentRunsForSameAgentAreSafe passes on POSIX; the Windows path compiles cleanly (GOOS=windows go build/vet).

Reviewer note: The reviewer noted that isRetryableRenameErr has no Windows unit test (since CI doesn't run Windows-tagged tests on Linux). The function is a single errors.Is call with no branch; correctness is covered by the concurrent integration test in Windows CI.

On Windows, os.Rename fails with ERROR_ACCESS_DENIED when another
goroutine holds an open handle to the directory being renamed aside.
Concurrent Build() calls for the same agent hit this in the retire step
of promote(): one goroutine holds finalDir open while another tries to
move it to a .old-... sibling.

Add a build-tagged helper isRetryableRenameErr() — Windows checks
errors.Is(err, syscall.ERROR_ACCESS_DENIED); all other platforms return
false unconditionally so POSIX EACCES is still a hard error. In the
retire-rename error path, treat a retryable error as a loop continuation
rather than a hard failure: increment attempt, yield (runtime.Gosched)
so the competing goroutine can release its handle, then retry with a
fresh unique sibling name.

Fixes TestBuild_ConcurrentRunsForSameAgentAreSafe on Windows (CI run
31013010316, job 92330933439).
@aheritier
aheritier marked this pull request as ready for review August 5, 2026 20:29
@aheritier
aheritier requested a review from a team as a code owner August 5, 2026 20:29
@aheritier aheritier added area/runtime Runtime engine, agent loop execution, tool dispatch, loop detection kind/fix PR fixes a bug (maps to fix:). Use on PRs only. labels Aug 5, 2026
@aheritier
aheritier requested a review from docker-agent August 5, 2026 20:37
@Sayt-0
Sayt-0 merged commit c2d35a7 into main Aug 6, 2026
17 checks passed
@Sayt-0
Sayt-0 deleted the fix/windows-kit-promote-rename-race branch August 6, 2026 08:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/runtime Runtime engine, agent loop execution, tool dispatch, loop detection kind/fix PR fixes a bug (maps to fix:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants