Repository navigation
fix: Abort connection retries on permanent socket errors and report the syscall error verbatim - #2019
Merged
hatayama merged 4 commits intoJul 27, 2026
Conversation
A connect refused with EPERM or EACCES cannot become reachable while the caller waits, but both dial-retry paths treated every connection attempt error as transient. The project runner spent its full 60-second unity-alive window on it and then reported the window's own deadline as `i/o timeout`, so the syscall error the first attempt already had never reached the caller; the readiness wait behaved the same way over its own timeout. The reported envelope then advised waiting and retrying a condition that never clears, which is what sent a 2026-07-26 investigation after a healthy Editor. Both loops now stop at the first refused connect and report that error, and its envelope states the syscall text verbatim with Retryable and SafeToRetry false plus guidance pointing at sandboxing and socket permissions.
…ansport scope The Symptom section described the old misdiagnosis as current behavior, which would now read as a bug against the fixed CLI. It also implied the block was a Unix socket property, and that reading produced the wrong inference that a TCP transport would get through: with the default allowedHosts policy the sandbox stops localhost TCP too, and a plain `uloop` command succeeds only because excludedCommands removes it from the sandbox. The stamp files move because this change touches non-test cli/common sources, which CI requires to reach every affected release.
Contributor
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds permanent connect-error classification for permission-denied socket failures, propagates those errors through readiness and pause polling, prevents retries, expands coverage, updates generated input hashes, and revises sandbox failure documentation. ChangesPermanent connection error handling
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant ReadinessPolling
participant ConnectionClassifier
participant RetryLoop
CLI->>ReadinessPolling: probe or query status
ReadinessPolling->>ConnectionClassifier: classify connection error
ConnectionClassifier-->>ReadinessPolling: permanent refusal
ReadinessPolling-->>CLI: return error immediately
CLI->>RetryLoop: send connection request
RetryLoop->>ConnectionClassifier: classify connection error
ConnectionClassifier-->>RetryLoop: permanent refusal
RetryLoop-->>CLI: abort without retry wait
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…-connect abort Matching EPERM and EACCES left Windows out: a named pipe reports access denial as ERROR_ACCESS_DENIED, which maps to os.ErrPermission but to neither POSIX errno, so the refusal stayed retryable there and the 60-second window still replaced it with its own deadline error. The classification now tests os.ErrPermission, scoped to a connection attempt so file permission failures reaching the same callers are not mistaken for a refused dial. The pause-point wait dials on every poll as well, so a refused connect spent the whole --timeout before reporting itself; it now aborts at the first poll. The readiness test's own timeout is shorter than one poll interval, so a regression fails on its assertions instead of hanging until the package deadline.
The cross-platform test substitutes os.ErrPermission for the status go-winio actually returns, so it proves the classifier's branch but not the assumption underneath it: that Go maps ERROR_ACCESS_DENIED to that target. This test uses the status code itself, so the assumption fails loudly on the Windows CI job instead of leaving Windows silently retrying a refusal that never clears.
hatayama
merged commit Jul 27, 2026
3f15d79
into
feature/cli-discoverability-integration
1 of 2 checks passed
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.
Summary
uloopcommand whose connection the operating system refuses now fails immediately and names the real reason, instead of retrying for 60 seconds and reportingi/o timeout.Retryable: false,SafeToRetry: false) with next actions that point at sandboxing and socket permissions rather than at waiting.User Impact
Before: when
connect()was refused with EPERM/EACCES — an agent's sandboxed shell denying the Unix socket, or wrong permissions on the socket path — the project runner kept retrying for its full 60-second window and then reported the window's own deadline expiry:The per-attempt error (
connect: operation not permitted) existed only in the debug VibeLog. Following the advice cost 60 seconds per attempt and sent one investigation after a Unity Editor that was healthy the whole time.After: the first refused attempt ends the command and reports itself:
The same abort applies to the readiness wait used by
uloop launch, which previously polled out its own timeout under the same condition.Dial failures the retry window exists for — the socket not created yet, nobody listening yet, a deadline expiry — keep being retried unchanged.
Changes
os.ErrPermissionso a Windows named pipe'sERROR_ACCESS_DENIEDcounts as well as POSIX EPERM/EACCES, scoped to a connection attempt so file permission failures reaching the same callers are not mistaken for a refused dial.--timeouton it.docs/claude-code-sandbox.md: the Symptom section now describes the new output (and marksi/o timeoutas evidence of an older CLI); the Cause section records that the block is not transport-specific — with the defaultallowedHostspolicy localhost TCP is stopped too, and a plainuloop ...command works only becauseexcludedCommandstakes it out of the sandbox.scripts/stamp-release-inputs.shrun in this PR, since non-testcli/commonsources changed.Verification
scripts/check-go-cli.sh— exit 0,0 issues.for every module, noFAILlines.TestSendWithTransientConnectionRetryAbortsOnRefusedConnect): a real listening Unix socket chmod'ed to000, so the errno comes from the kernel. Reverting the retry-predicate change reproduces the incident exactly and fails the test:--- FAIL (60.01s) ... not responding: ... i/o timeout. With the fix it passes in 0.00s.TestShouldNotRetryPermanentlyRefusedConnectionred; readiness abort →TestWaitForToolReadinessReturnsPermanentlyRefusedConnectImmediatelyred in 0.10s with the wrapped timeout error; pause-point abort →TestWaitForPausePointAbortsWhenTheConnectIsRefusedred after 7432 polls over the full 10s timeout; envelope branch →TestClassifyConnectionAttemptErrorForRefusedConnectred withRetryable:true, SafeToRetry:true; classification narrowed back to the two POSIX errnos → the named-pipe test red, and dropped the connection-attempt scope → the file-permission test red.GOOS=windows go vetpasses for the touched packages in both modules.dist/darwin-arm64/uloop compilesucceeded), so the live EPERM environment that produced the incident is not reproducible here; the socket-permission test above exercises the same errno path through the same code.scripts/check-go-cli.shrun above is the equivalent evidence, and CI runs on the final integration pull request.