Skip to content

Issue #180: surface IPC socket conflicts instead of failing silently - #207

Open
PRINCEKK122 wants to merge 3 commits into
oss-slu:mainfrom
PRINCEKK122:180-ipc-socket-in-use
Open

PRINCEKK122 wants to merge 3 commits into
oss-slu:mainfrom
PRINCEKK122:180-ipc-socket-in-use

Conversation

@PRINCEKK122

Copy link
Copy Markdown
Contributor

Closes #180.

Draft — the code change is complete and builds, but the retry path is not yet verified. Opening early for visibility on the approach. I will run the check and mark ready for review.

Problem

ipc_connect created the socket listener inside the thread it spawned, so a bind failure had no route back to the caller. Three things went wrong together:

  1. The AddrInUse arm returned Ok(()) (state.rs:380) — the thread reported success having bound nothing. The neighbouring error arm returned Err, so this case was singled out to be swallowed.
  2. ipc_thread_handle was set to Some unconditionally (state.rs:501), for a thread that owned no listener.
  3. The is_some() guard at the top of ipc_connect then rejected every later Connect IPC press, for the life of the process.

Since ipc_connect runs at startup (main.rs:37), the relay booted straight into this state whenever baton.sock was occupied — by a second instance or a stale socket. The only symptom was :( No Baton Connection, indistinguishable from the plugin not running.

Change

Bind the listener on the calling thread, before spawning:

  • create_sync() and set_nonblocking() now run in ipc_connect and return Err on failure, so the existing handlers in main.rs:37 and update.rs:112 surface it. No new plumbing.
  • The AddrInUse special case is gone; it is now an ordinary error with a message that names the likely cause rather than describing a "socket file", which is misleading on Linux where this is an abstract namespace socket.
  • set_nonblocking's .expect() — previously a thread panic — is a returned error.
  • ipc_thread_handle is only assigned once a bound listener exists, so retry works: free the socket, press Connect IPC, it connects.

The thread body is unchanged apart from receiving the listener rather than creating it.

Testing

Built and ran on Linux. Two concurrent relay instances start without the second panicking or hanging.

The retry path — close the first instance, press Connect IPC on the second, confirm it acquires the socket — is still to be verified, and I will confirm on this PR before it merges.

Note that the error text is not yet visible in the GUI on main: ipc_connect's failure goes to event_log, which nothing renders until #203 lands. This PR makes the error exist and be reportable; #203 makes it visible. They are independent but complementary.

🤖 Generated with Claude Code

…ling silently

ipc_connect created the listener inside the thread it spawned, so a bind
failure could not reach the caller. The AddrInUse arm returned Ok(()),
reporting success having bound nothing, and ipc_thread_handle was set
regardless. The is_some() guard then rejected every later Connect IPC
press for the life of the process. Since ipc_connect runs at startup,
the relay booted into this state whenever baton.sock was occupied, with
":( No Baton Connection" as the only symptom.

Bind the listener on the calling thread instead. create_sync() and
set_nonblocking() now return Err, which the existing handlers in main.rs
and update.rs already report, and the thread handle is only assigned
once a bound listener exists, so retry works after the socket is freed.
@Sanjana-Siddani
Sanjana-Siddani self-requested a review September 28, 2026 04:15

@Sanjana-Siddani Sanjana-Siddani left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hey Prince, nice fix! Moving the bind out of the thread makes sense. Before, if baton.sock was already taken, the thread just returned Ok but the handle still got set, so hitting Connect again always said "IPC thread already exists".

I tested it locally with two relays at the same time. With your change the second one shows the "already in use" error, and connecting again works once the first one is closed. On main it just silently says it worked. It builds fine too, and the error shows up in the event log in the GUI now.

One thing before merging: there's a small conflict in state.rs with the emoji-removal PR (#204). It just changed the log messages in the block you removed, so you can keep your version and drop that block.

Approving, just merge once the conflict is fixed

@PRINCEKK122
PRINCEKK122 marked this pull request as ready for review October 15, 2026 03:02
@PRINCEKK122

Copy link
Copy Markdown
Contributor Author

Rebased past the conflict with #204 - it was inside the block this PR deletes, so nothing from it was lost. Also merged current main, which pulled in #210 and #205. The ipc_connect changeitself is unchanged from your approval; re-requesting since the new commits dismissed it.

@Sanjana-Siddani
Sanjana-Siddani self-requested a review October 18, 2026 06:12

@Sanjana-Siddani Sanjana-Siddani left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for fixing the conflict! The ipc_connect change is the same as before, all 9 relay tests pass, and the two-relay test still shows the “already in use” error and lets you retry. Small thing: the README troubleshooting row still says the relay “gives up silently” for a busy socket, which isn’t true anymore. Could update that later. Approving again 👍

@PRINCEKK122

Copy link
Copy Markdown
Contributor Author

All four of my open PRs are approved. #207 and #243 are CLEAN and ready whenever someone has a moment; I'm refreshing #234 and #242 against main tonight after #247 landed.

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.

Relay starts in a permanently broken IPC state when baton.sock is already in use

2 participants