ScuttleBot

fix: restart mirror and input loops after SIGUSR1 reconnection The SIGUSR1 handler now restarts mirrorSessionLoop and relayInputLoop with the new connector after reconnecting. Previously only the IRC connection was swapped but the mirror/input goroutines held stale references to the old connector, causing "mirror failed: context deadline exceeded" errors.

lmata 2026-04-03 23:52 trunk
Commit c0cc5deead6d9c39a09d90fd0f949f1eab85fcb2ae630aef4018be1cea7d33ad
1 file changed +8 -3
--- cmd/claude-relay/main.go
+++ cmd/claude-relay/main.go
@@ -196,11 +196,10 @@
196196
"SCUTTLEBOT_ACTIVITY_VIA_BROKER="+boolString(relayActive),
197197
)
198198
if relayActive {
199199
go mirrorSessionLoop(ctx, relay, cfg, startedAt)
200200
go presenceLoopPtr(ctx, &relay, cfg.HeartbeatInterval)
201
- go handleReconnectSignal(ctx, &relay, cfg)
202201
}
203202
204203
if !isInteractiveTTY() {
205204
cmd.Stdin = os.Stdin
206205
cmd.Stdout = os.Stdout
@@ -251,10 +250,11 @@
251250
go func() {
252251
copyPTYOutput(ptmx, os.Stdout, state)
253252
}()
254253
if relayActive {
255254
go relayInputLoop(ctx, relay, cfg, state, ptmx, onlineAt)
255
+ go handleReconnectSignal(ctx, &relay, cfg, state, ptmx, startedAt)
256256
}
257257
258258
err = cmd.Wait()
259259
cancel()
260260
@@ -639,11 +639,11 @@
639639
}
640640
641641
// handleReconnectSignal listens for SIGUSR1 and tears down/rebuilds
642642
// the IRC connection. The relay-watchdog sidecar sends this signal
643643
// when it detects the server restarted or the network is down.
644
-func handleReconnectSignal(ctx context.Context, relayPtr *sessionrelay.Connector, cfg config) {
644
+func handleReconnectSignal(ctx context.Context, relayPtr *sessionrelay.Connector, cfg config, state *relayState, ptmx *os.File, startedAt time.Time) {
645645
sigCh := make(chan os.Signal, 1)
646646
signal.Notify(sigCh, syscall.SIGUSR1)
647647
defer signal.Stop(sigCh)
648648
649649
for {
@@ -694,15 +694,20 @@
694694
continue
695695
}
696696
cancel()
697697
698698
*relayPtr = conn
699
+ now := time.Now()
699700
_ = conn.Post(context.Background(), fmt.Sprintf(
700701
"reconnected in %s; mention %s to interrupt",
701702
filepath.Base(cfg.TargetCWD), cfg.Nick,
702703
))
703
- fmt.Fprintf(os.Stderr, "claude-relay: reconnected successfully\n")
704
+ fmt.Fprintf(os.Stderr, "claude-relay: reconnected, restarting mirror and input loops\n")
705
+
706
+ // Restart mirror and input loops with the new connector.
707
+ go mirrorSessionLoop(ctx, conn, cfg, startedAt)
708
+ go relayInputLoop(ctx, conn, cfg, state, ptmx, now)
704709
break
705710
}
706711
}
707712
}
708713
709714
--- cmd/claude-relay/main.go
+++ cmd/claude-relay/main.go
@@ -196,11 +196,10 @@
196 "SCUTTLEBOT_ACTIVITY_VIA_BROKER="+boolString(relayActive),
197 )
198 if relayActive {
199 go mirrorSessionLoop(ctx, relay, cfg, startedAt)
200 go presenceLoopPtr(ctx, &relay, cfg.HeartbeatInterval)
201 go handleReconnectSignal(ctx, &relay, cfg)
202 }
203
204 if !isInteractiveTTY() {
205 cmd.Stdin = os.Stdin
206 cmd.Stdout = os.Stdout
@@ -251,10 +250,11 @@
251 go func() {
252 copyPTYOutput(ptmx, os.Stdout, state)
253 }()
254 if relayActive {
255 go relayInputLoop(ctx, relay, cfg, state, ptmx, onlineAt)
 
256 }
257
258 err = cmd.Wait()
259 cancel()
260
@@ -639,11 +639,11 @@
639 }
640
641 // handleReconnectSignal listens for SIGUSR1 and tears down/rebuilds
642 // the IRC connection. The relay-watchdog sidecar sends this signal
643 // when it detects the server restarted or the network is down.
644 func handleReconnectSignal(ctx context.Context, relayPtr *sessionrelay.Connector, cfg config) {
645 sigCh := make(chan os.Signal, 1)
646 signal.Notify(sigCh, syscall.SIGUSR1)
647 defer signal.Stop(sigCh)
648
649 for {
@@ -694,15 +694,20 @@
694 continue
695 }
696 cancel()
697
698 *relayPtr = conn
 
699 _ = conn.Post(context.Background(), fmt.Sprintf(
700 "reconnected in %s; mention %s to interrupt",
701 filepath.Base(cfg.TargetCWD), cfg.Nick,
702 ))
703 fmt.Fprintf(os.Stderr, "claude-relay: reconnected successfully\n")
 
 
 
 
704 break
705 }
706 }
707 }
708
709
--- cmd/claude-relay/main.go
+++ cmd/claude-relay/main.go
@@ -196,11 +196,10 @@
196 "SCUTTLEBOT_ACTIVITY_VIA_BROKER="+boolString(relayActive),
197 )
198 if relayActive {
199 go mirrorSessionLoop(ctx, relay, cfg, startedAt)
200 go presenceLoopPtr(ctx, &relay, cfg.HeartbeatInterval)
 
201 }
202
203 if !isInteractiveTTY() {
204 cmd.Stdin = os.Stdin
205 cmd.Stdout = os.Stdout
@@ -251,10 +250,11 @@
250 go func() {
251 copyPTYOutput(ptmx, os.Stdout, state)
252 }()
253 if relayActive {
254 go relayInputLoop(ctx, relay, cfg, state, ptmx, onlineAt)
255 go handleReconnectSignal(ctx, &relay, cfg, state, ptmx, startedAt)
256 }
257
258 err = cmd.Wait()
259 cancel()
260
@@ -639,11 +639,11 @@
639 }
640
641 // handleReconnectSignal listens for SIGUSR1 and tears down/rebuilds
642 // the IRC connection. The relay-watchdog sidecar sends this signal
643 // when it detects the server restarted or the network is down.
644 func handleReconnectSignal(ctx context.Context, relayPtr *sessionrelay.Connector, cfg config, state *relayState, ptmx *os.File, startedAt time.Time) {
645 sigCh := make(chan os.Signal, 1)
646 signal.Notify(sigCh, syscall.SIGUSR1)
647 defer signal.Stop(sigCh)
648
649 for {
@@ -694,15 +694,20 @@
694 continue
695 }
696 cancel()
697
698 *relayPtr = conn
699 now := time.Now()
700 _ = conn.Post(context.Background(), fmt.Sprintf(
701 "reconnected in %s; mention %s to interrupt",
702 filepath.Base(cfg.TargetCWD), cfg.Nick,
703 ))
704 fmt.Fprintf(os.Stderr, "claude-relay: reconnected, restarting mirror and input loops\n")
705
706 // Restart mirror and input loops with the new connector.
707 go mirrorSessionLoop(ctx, conn, cfg, startedAt)
708 go relayInputLoop(ctx, conn, cfg, state, ptmx, now)
709 break
710 }
711 }
712 }
713
714

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button