Skip to content

Fix intermittent CLI socket-not-found errors for tagged/debug runs - #832

Merged
austinywang merged 1 commit into
mainfrom
fix/811-socket-not-found
Mar 5, 2026
Merged

austinywang merged 1 commit into
mainfrom
fix/811-socket-not-found

Conversation

@austinywang

@austinywang austinywang commented Mar 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add CLI socket path autodiscovery for default-path calls (/tmp/cmux.sock) so tagged/debug sockets are found
  • treat CMUX_SOCKET_PATH=/tmp/cmux.sock as implicit default to avoid false hard-fail when caller env is stale
  • add short connect retry window to handle listener startup races
  • add a CLI regression test covering tagged socket autodiscovery via CMUX_TAG

Testing

  • xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination 'platform=macOS' build
  • ./scripts/reload.sh --tag issue-811-socket
  • CMUX_CLI_BIN=/tmp/cmux-issue-811-socket/Build/Products/Debug/cmux python3 tests/test_cli_socket_autodiscovery.py
  • CMUX_CLI_BIN=/tmp/cmux-issue-811-socket/Build/Products/Debug/cmux python3 tests/test_claude_hook_missing_socket_error.py
  • python3 tests/test_cli_socket_sentry_scope.py

Closes #811


Summary by cubic

Fix intermittent CLI “socket not found” errors for tagged/debug runs by auto-discovering the correct socket and retrying connects during startup. Addresses #811.

  • Bug Fixes
    • Auto-discovers tagged/debug sockets when the default path is used (or CMUX_SOCKET_PATH equals the default), preferring active sockets and then recent/existing ones.
    • Treats /tmp/cmux.sock as an implicit default to avoid false failures from stale environment variables.
    • Adds a short connect retry window (2s) to cover listener startup races for common transient errors.
    • Adds a regression test for autodiscovery and updates CLI help and telemetry breadcrumbs.

Written for commit 54cd0c3. Summary will update on new commits.

Summary by CodeRabbit

  • New Features
    • Socket auto-discovery: CLI now automatically discovers and connects to sockets tagged via the CMUX_TAG environment variable, completely removing the need for manual socket path specification
    • Connection resilience: Enhanced retry logic automatically handles transient socket connection errors within a configurable window, with improved socket validation and error handling

@vercel

vercel Bot commented Mar 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Mar 4, 2026 3:12am

@coderabbitai

coderabbitai Bot commented Mar 4, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Implements socket autodiscovery for the CLI with a new resolver that locates tagged sockets, validates ownership, and retries connections on transient errors. Adds socket discovery capabilities including last-used path reading and tagged socket discovery in /tmp, alongside a regression test validating end-to-end autodiscovery behavior.

Changes

Cohort / File(s) Summary
Socket Autodiscovery & Retry Logic
CLI/cmux.swift
Introduces CLISocketPathSource enum and CLISocketPathResolver for autodiscovering tagged sockets with fallback logic. Extends SocketClient with configurable retry behavior, socket ownership/type validation, and telemetry breadcrumbs for discovery and connection events. Updates CLI initialization to resolve socket paths via the resolver and use resolved paths for all socket interactions.
Autodiscovery Regression Test
tests/test_cli_socket_autodiscovery.py
Adds test module validating CLI autodiscovery of tagged sockets using CMUX_TAG. Includes resolve_cmux_cli() helper, PingServer UNIX-domain socket server running in a thread, and end-to-end test coordinating server startup, CLI invocation with environment setup, and output validation.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI Entry Point
    participant Resolver as Socket Path Resolver
    participant Discovery as Socket Discovery
    participant Client as SocketClient
    participant Socket as Tagged Socket
    
    CLI->>Resolver: Resolve socket path (CMUX_TAG, env, etc.)
    Resolver->>Discovery: Discover tagged sockets in /tmp
    Discovery->>Discovery: Filter by CMUX_TAG
    Discovery-->>Resolver: Candidate socket paths
    Resolver->>Socket: Validate socket ownership & type
    Socket-->>Resolver: Validation result
    Resolver-->>CLI: Resolved socket path + source
    
    CLI->>Client: Connect to resolved socket
    Client->>Socket: Attempt connection
    alt Connection fails (transient error)
        Socket-->>Client: Error (EAGAIN, ENOENT, etc.)
        Client->>Client: Within retry window?
        Client->>Socket: Retry connection
        Socket-->>Client: Success
    else Connection succeeds
        Socket-->>Client: Connected
    end
    Client-->>CLI: Connection established
    CLI->>Socket: Send ping command
    Socket-->>CLI: Receive PONG response
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

  • PR #705 — Modifies CLI/cmux.swift startup and connection flow; affects overlapping socket detection and early path-based handling logic before socket connection.

Poem

🐰 A socket sleeps in /tmp with a tag so fine,
The resolver hops and sniffs—is this socket mine?
With retries and breadcrumbs, no race shall we lose,
The CLI finds its path, no more the Socket Blues! 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: fixing intermittent socket-not-found errors for tagged/debug runs through autodiscovery.
Linked Issues check ✅ Passed The PR addresses all coding requirements from #811: socket autodiscovery for tagged sockets, retry logic for connection races, and implicit default path handling.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing socket discovery and connection reliability. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/811-socket-not-found

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds CLI socket path autodiscovery to resolve intermittent socket-not-found errors when using tagged/debug builds. The implementation includes:

  • CLISocketPathResolver with intelligent candidate socket discovery based on CMUX_TAG env var, filesystem scanning of /tmp/cmux*.sock files, and fallback paths
  • Connection retry logic with a 2-second window (100ms intervals) to handle listener startup race conditions
  • Telemetry breadcrumbs for debugging socket path resolution (socket.path.autodiscovered)
  • Comprehensive test coverage validating autodiscovery via the new test_cli_socket_autodiscovery.py

The autodiscovery only activates when socket path is implicitly defaulted (not explicitly set via --socket flag or non-default CMUX_SOCKET_PATH env var). Security checks (socket ownership validation) remain intact.

Minor cleanup needed: unreachable code at CLI/cmux.swift:684 can be removed.

Confidence Score: 4/5

  • safe to merge with minor cleanup recommended
  • well-architected solution with good test coverage and preserved security checks, one unreachable code line should be cleaned up but doesn't affect functionality
  • CLI/cmux.swift has unreachable code at line 684 that should be removed

Important Files Changed

Filename Overview
CLI/cmux.swift adds socket path autodiscovery resolver and retry logic with minor unreachable code at line 684
tests/test_cli_socket_autodiscovery.py comprehensive regression test for tagged socket autodiscovery via CMUX_TAG env var

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start[CLI invoked] --> CheckSource{Socket path<br/>source?}
    CheckSource -->|explicitFlag or<br/>non-default env| UseRequested[Use requested path]
    CheckSource -->|implicitDefault| BuildCandidates[Build candidate list]
    
    BuildCandidates --> CheckTag{CMUX_TAG<br/>set?}
    CheckTag -->|Yes| AddTagged[Add /tmp/cmux-debug-tag.sock<br/>/tmp/cmux-tag.sock]
    CheckTag -->|No| AddDefaults[Add /tmp/cmux.sock]
    AddTagged --> AddDefaults
    AddDefaults --> AddFallbacks[Add /tmp/cmux-debug.sock<br/>/tmp/cmux-staging.sock]
    AddFallbacks --> ScanFS[Scan /tmp for cmux*.sock<br/>sort by mtime]
    ScanFS --> TryConnect{Try connect<br/>to candidates}
    
    TryConnect -->|Connected| Return[Use connected socket]
    TryConnect -->|No connection| CheckFiles{Socket files<br/>exist?}
    CheckFiles -->|Yes| UseFirst[Use first existing socket]
    CheckFiles -->|No| UseRequested
    UseFirst --> ConnectRetry
    UseRequested --> ConnectRetry
    Return --> ConnectRetry
    
    ConnectRetry[Connect with retry] --> VerifyOwner{Owner is<br/>current user?}
    VerifyOwner -->|No| ThrowError[Throw security error]
    VerifyOwner -->|Yes| AttemptConnect[Attempt connect]
    AttemptConnect -->|Success| Done[Connected]
    AttemptConnect -->|ENOENT/ECONNREFUSED<br/>within 2s window| Sleep[Sleep 100ms]
    Sleep --> ConnectRetry
    AttemptConnect -->|Other error or<br/>timeout| ThrowError
Loading

Last reviewed commit: 54cd0c3

Comment thread CLI/cmux.swift
throw error
}

throw lastError ?? CLIError(message: "Failed to connect to socket at \(path)")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unreachable code - the while true loop above only exits via return (line 668) or throw (lines 638, 641, 644, 649, 681), so this line can never execute

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/test_cli_socket_autodiscovery.py">

<violation number="1" location="tests/test_cli_socket_autodiscovery.py:123">
P2: The test can pass without ever receiving the ping on the tagged test socket because the server thread is only joined for 2 seconds.</violation>
</file>

<file name="CLI/cmux.swift">

<violation number="1" location="CLI/cmux.swift:539">
P2: Socket autodiscovery can pick sockets not owned by the current user, which then fails immediately in `SocketClient.connect()` and can mask a valid socket candidate.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

print(f"FAIL: invoking cmux ping failed: {exc}")
return 1
finally:
server.join(timeout=2.0)

@cubic-dev-ai cubic-dev-ai Bot Mar 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The test can pass without ever receiving the ping on the tagged test socket because the server thread is only joined for 2 seconds.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test_cli_socket_autodiscovery.py, line 123:

<comment>The test can pass without ever receiving the ping on the tagged test socket because the server thread is only joined for 2 seconds.</comment>

<file context>
@@ -0,0 +1,150 @@
+        print(f"FAIL: invoking cmux ping failed: {exc}")
+        return 1
+    finally:
+        server.join(timeout=2.0)
+        try:
+            os.remove(socket_path)
</file context>
Fix with Cubic

Comment thread CLI/cmux.swift
Comment on lines +539 to +542
private static func isSocketFile(_ path: String) -> Bool {
var st = stat()
return lstat(path, &st) == 0 && (st.st_mode & mode_t(S_IFMT)) == mode_t(S_IFSOCK)
}

@cubic-dev-ai cubic-dev-ai Bot Mar 4, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Socket autodiscovery can pick sockets not owned by the current user, which then fails immediately in SocketClient.connect() and can mask a valid socket candidate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At CLI/cmux.swift, line 539:

<comment>Socket autodiscovery can pick sockets not owned by the current user, which then fails immediately in `SocketClient.connect()` and can mask a valid socket candidate.</comment>

<file context>
@@ -451,9 +451,159 @@ private enum SocketPasswordResolver {
+        return discovered.prefix(limit).map(\.path)
+    }
+
+    private static func isSocketFile(_ path: String) -> Bool {
+        var st = stat()
+        return lstat(path, &st) == 0 && (st.st_mode & mode_t(S_IFMT)) == mode_t(S_IFSOCK)
</file context>
Suggested change
private static func isSocketFile(_ path: String) -> Bool {
var st = stat()
return lstat(path, &st) == 0 && (st.st_mode & mode_t(S_IFMT)) == mode_t(S_IFSOCK)
}
private static func isSocketFile(_ path: String) -> Bool {
var st = stat()
return lstat(path, &st) == 0 &&
(st.st_mode & mode_t(S_IFMT)) == mode_t(S_IFSOCK) &&
st.st_uid == getuid()
}
Fix with Cubic

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/test_cli_socket_autodiscovery.py (1)

15-17: Fail fast when CMUX_CLI_BIN/CMUX_CLI is set but invalid.

If an explicit path is provided but not executable, silently falling back can make this regression run against an unintended binary.

🔧 Proposed fix
 def resolve_cmux_cli() -> str:
     explicit = os.environ.get("CMUX_CLI_BIN") or os.environ.get("CMUX_CLI")
-    if explicit and os.path.exists(explicit) and os.access(explicit, os.X_OK):
-        return explicit
+    if explicit:
+        if os.path.exists(explicit) and os.access(explicit, os.X_OK):
+            return explicit
+        raise RuntimeError(f"CMUX_CLI_BIN/CMUX_CLI is not executable: {explicit}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_cli_socket_autodiscovery.py` around lines 15 - 17, The code
currently falls back silently when an explicit CLI path (from env vars
CMUX_CLI_BIN or CMUX_CLI) is provided but missing or not executable; update the
logic around the explicit variable to fail fast: after reading explicit, if
explicit is truthy and (not os.path.exists(explicit) or not os.access(explicit,
os.X_OK)), raise a clear error (e.g., raise RuntimeError or SystemExit with a
message referencing the env var and path) instead of returning None or
continuing, so callers see the misconfigured CMUX_CLI_BIN/CMUX_CLI immediately.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLI/cmux.swift`:
- Around line 478-485: When CMUX_TAG is set, the selection logic for candidates
must prefer the tagged socket instead of returning the first connectable path;
change the loops that currently iterate over candidates and call canConnect(to:)
/ isSocketFile(path) so they first filter candidates to those matching the
CMUX_TAG (the tag-matching predicate used elsewhere in this file) and only
consider connectable or existing socket files within that filtered list, falling
back to the global candidate loops only if no tagged candidates are found;
update both the connectable-check loop (using canConnect(to:)) and the
existing-socket loop (using isSocketFile(path)) to implement this behavior.
- Around line 553-557: Before calling strncpy on addr.sun_path, validate the
path length against the capacity of sockaddr_un.sun_path: compute maxLength =
MemoryLayout.size(ofValue: addr.sun_path) (or use MemoryLayout.size(ofValue:
addr.sun_path) - 1 if you reserve a NUL), check path.utf8.count < maxLength (or
<= maxLength-1) and if it exceeds, return/throw an error (or propagate a
failure) instead of truncating; apply this check in the canConnect code path
that uses path.withCString/addr.sun_path/strncpy and in the socket connection
method that does the same, so neither call silently truncates into a different
socket path.

In `@tests/test_cli_socket_autodiscovery.py`:
- Around line 123-131: The test can report a false PASS because
server.join(timeout=2.0) may return before the server thread's accept (6s)
completes; change the teardown to wait for the server thread to fully finish
before removing socket_path and checking server.error — e.g., call server.join()
without a short timeout or use a timeout >6s (e.g., 8s) and/or assert not
server.is_alive() before os.remove(socket_path); ensure the error check uses
server.error after this guaranteed join so the test can't pass while the server
is still running.

---

Nitpick comments:
In `@tests/test_cli_socket_autodiscovery.py`:
- Around line 15-17: The code currently falls back silently when an explicit CLI
path (from env vars CMUX_CLI_BIN or CMUX_CLI) is provided but missing or not
executable; update the logic around the explicit variable to fail fast: after
reading explicit, if explicit is truthy and (not os.path.exists(explicit) or not
os.access(explicit, os.X_OK)), raise a clear error (e.g., raise RuntimeError or
SystemExit with a message referencing the env var and path) instead of returning
None or continuing, so callers see the misconfigured CMUX_CLI_BIN/CMUX_CLI
immediately.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5d0316a5-2cda-43c2-b363-cc3fbfbf8cf7

📥 Commits

Reviewing files that changed from the base of the PR and between 2f6cb6f and 54cd0c3.

📒 Files selected for processing (2)
  • CLI/cmux.swift
  • tests/test_cli_socket_autodiscovery.py

Comment thread CLI/cmux.swift
Comment on lines +478 to +485
for path in candidates where canConnect(to: path) {
return path
}

// If the listener is still starting, prefer existing socket files.
for path in candidates where isSocketFile(path) {
return path
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

CMUX_TAG can still route commands to the wrong instance during startup race.

With CMUX_TAG present (Lines 493-497), Line 478 still returns the first connectable socket across all candidates. If the tagged socket is not yet accepting connections but /tmp/cmux.sock is, the CLI can connect to the wrong instance.

🔧 Proposed fix
     static func resolve(
         requestedPath: String,
         source: CLISocketPathSource,
         environment: [String: String] = ProcessInfo.processInfo.environment
     ) -> String {
         guard source == .implicitDefault else {
             return requestedPath
         }

-        let candidates = dedupe(candidatePaths(requestedPath: requestedPath, environment: environment))
+        let tagged = dedupe(taggedCandidatePaths(environment: environment))
+        let candidates = dedupe(candidatePaths(requestedPath: requestedPath, environment: environment))

+        // When CMUX_TAG is set, prefer tagged sockets first (including startup race fallback).
+        if !tagged.isEmpty {
+            for path in tagged where canConnect(to: path) { return path }
+            for path in tagged where isSocketFile(path) { return path }
+        }
+
         // Prefer sockets that are currently accepting connections.
         for path in candidates where canConnect(to: path) {
             return path
         }
@@
     }
+
+    private static func taggedCandidatePaths(environment: [String: String]) -> [String] {
+        guard let tag = normalized(environment["CMUX_TAG"]) else { return [] }
+        let slug = sanitizeTagSlug(tag)
+        return [
+            "/tmp/cmux-debug-\(slug).sock",
+            "/tmp/cmux-\(slug).sock"
+        ]
+    }

Also applies to: 493-497

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 478 - 485, When CMUX_TAG is set, the selection
logic for candidates must prefer the tagged socket instead of returning the
first connectable path; change the loops that currently iterate over candidates
and call canConnect(to:) / isSocketFile(path) so they first filter candidates to
those matching the CMUX_TAG (the tag-matching predicate used elsewhere in this
file) and only consider connectable or existing socket files within that
filtered list, falling back to the global candidate loops only if no tagged
candidates are found; update both the connectable-check loop (using
canConnect(to:)) and the existing-socket loop (using isSocketFile(path)) to
implement this behavior.

Comment thread CLI/cmux.swift
Comment on lines +553 to +557
path.withCString { ptr in
withUnsafeMutablePointer(to: &addr.sun_path) { pathPtr in
let buf = UnsafeMutableRawPointer(pathPtr).assumingMemoryBound(to: CChar.self)
strncpy(buf, ptr, maxLength - 1)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

head -c 50000 CLI/cmux.swift | tail -c +20000 | wc -l

Repository: manaflow-ai/cmux

Length of output: 63


🏁 Script executed:

wc -l CLI/cmux.swift

Repository: manaflow-ai/cmux

Length of output: 79


🏁 Script executed:

sed -n '540,570p' CLI/cmux.swift

Repository: manaflow-ai/cmux

Length of output: 1317


🏁 Script executed:

sed -n '640,670p' CLI/cmux.swift

Repository: manaflow-ai/cmux

Length of output: 1389


🏁 Script executed:

rg -n -C3 'sockaddr_un|sun_path' CLI/cmux.swift

Repository: manaflow-ai/cmux

Length of output: 1820


🏁 Script executed:

rg -n 'lengthOfBytes|ENAMETOOLONG|Socket path is too long' CLI/cmux.swift

Repository: manaflow-ai/cmux

Length of output: 42


Validate sun_path length before copying socket paths.

Path bytes are copied with strncpy at lines 553 and 655 without validating against sockaddr_un.sun_path capacity. Overlong paths are silently truncated, causing attempts to connect to a different (truncated) socket.

Add a length check before each strncpy call:

Proposed fixes

Line 553 (canConnect method):

         let maxLength = MemoryLayout.size(ofValue: addr.sun_path)
+        guard path.lengthOfBytes(using: .utf8) < maxLength else { return false }
         path.withCString { ptr in

Line 655 (socket connection method):

         let maxLength = MemoryLayout.size(ofValue: addr.sun_path)
+        guard path.lengthOfBytes(using: .utf8) < maxLength else {
+            throw CLIError(message: "Socket path is too long: \(path)")
+        }
         path.withCString { ptr in
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
path.withCString { ptr in
withUnsafeMutablePointer(to: &addr.sun_path) { pathPtr in
let buf = UnsafeMutableRawPointer(pathPtr).assumingMemoryBound(to: CChar.self)
strncpy(buf, ptr, maxLength - 1)
}
let maxLength = MemoryLayout.size(ofValue: addr.sun_path)
guard path.lengthOfBytes(using: .utf8) < maxLength else { return false }
path.withCString { ptr in
withUnsafeMutablePointer(to: &addr.sun_path) { pathPtr in
let buf = UnsafeMutableRawPointer(pathPtr).assumingMemoryBound(to: CChar.self)
strncpy(buf, ptr, maxLength - 1)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLI/cmux.swift` around lines 553 - 557, Before calling strncpy on
addr.sun_path, validate the path length against the capacity of
sockaddr_un.sun_path: compute maxLength = MemoryLayout.size(ofValue:
addr.sun_path) (or use MemoryLayout.size(ofValue: addr.sun_path) - 1 if you
reserve a NUL), check path.utf8.count < maxLength (or <= maxLength-1) and if it
exceeds, return/throw an error (or propagate a failure) instead of truncating;
apply this check in the canConnect code path that uses
path.withCString/addr.sun_path/strncpy and in the socket connection method that
does the same, so neither call silently truncates into a different socket path.

Comment on lines +123 to +131
server.join(timeout=2.0)
try:
os.remove(socket_path)
except OSError:
pass

if server.error is not None:
print(f"FAIL: socket server error: {server.error}")
return 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Prevent false-positive PASS when the server thread may still be running.

Line 123 waits only 2 seconds, but the server accept timeout is 6 seconds (Line 57). If cmux ping succeeds against a different socket, server.error can still be nil at Line 129 and this test can pass incorrectly.

🔧 Proposed fix
 class PingServer:
@@
     def join(self, timeout: float) -> None:
         self._thread.join(timeout=timeout)
+
+    def is_alive(self) -> bool:
+        return self._thread.is_alive()
@@
     finally:
-        server.join(timeout=2.0)
+        server.join(timeout=7.0)
         try:
             os.remove(socket_path)
         except OSError:
             pass
+
+    if server.is_alive():
+        print("FAIL: socket server did not finish; ping may not have reached test socket")
+        return 1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_cli_socket_autodiscovery.py` around lines 123 - 131, The test can
report a false PASS because server.join(timeout=2.0) may return before the
server thread's accept (6s) completes; change the teardown to wait for the
server thread to fully finish before removing socket_path and checking
server.error — e.g., call server.join() without a short timeout or use a timeout
>6s (e.g., 8s) and/or assert not server.is_alive() before
os.remove(socket_path); ensure the error check uses server.error after this
guaranteed join so the test can't pass while the server is still running.

This branch was successfully deployed

1 active deployment
Preview — 54cd0c34 Deployed Mar 4, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cmux CLI intermittently fails with 'Socket not found at /tmp/cmux.sock'

1 participant