From ac34fc8342de9ee997d3a3a51afa2a5692e8022e Mon Sep 17 00:00:00 2001 From: David Aronchick Date: Tue, 27 Jan 2026 00:19:11 +0000 Subject: [PATCH] fix: detect running Claude before restarting to prevent session ID conflicts The 'multiclaude claude' command was failing with "Session ID already in use" error when Claude was already running in the agent context. This happened because the command would attempt to restart Claude with --session-id or --resume flags without checking if a Claude process was already active with that session ID. Changes: - Add process alive check for stored agent PID before restarting - Add double-check for any running process in the tmux pane - Provide helpful error messages with steps to exit and restart - Import syscall package for signal-based process detection The fix detects: 1. If the stored agent PID is still running 2. If a different process is running in the tmux pane Users now get clear instructions on how to properly restart Claude or attach to the existing session. Co-Authored-By: Claude Sonnet 4.5 --- internal/cli/cli.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index d1c8b02e..3f74402c 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -11,6 +11,7 @@ import ( "runtime/debug" "strconv" "strings" + "syscall" "time" "github.com/dlorenc/multiclaude/internal/agents" @@ -5042,6 +5043,7 @@ func (c *CLI) localRepair(verbose bool) error { } // restartClaude restarts Claude in the current agent context. +// It checks if Claude is already running and provides helpful error messages if so. // It auto-detects whether to use --resume or --session-id based on session history. func (c *CLI) restartClaude(args []string) error { // Infer agent context from cwd @@ -5065,6 +5067,41 @@ func (c *CLI) restartClaude(args []string) error { return fmt.Errorf("agent has no session ID - try removing and recreating the agent") } + // Check if Claude is already running + if agent.PID > 0 { + // Check if the process is still alive + process, err := os.FindProcess(agent.PID) + if err == nil { + // Send signal 0 to check if process exists (doesn't actually signal, just checks) + err = process.Signal(syscall.Signal(0)) + if err == nil { + // Process is still running - provide helpful error + return fmt.Errorf("Claude is already running (PID %d) in this context.\n\nTo restart:\n 1. Exit Claude first (Ctrl+D or /exit)\n 2. Then run 'multiclaude claude' again\n\nOr attach to the running session:\n multiclaude attach %s", agent.PID, agentName) + } + } + } + + // Get repo for tmux session info + repo, exists := st.GetRepo(repoName) + if !exists { + return fmt.Errorf("repo '%s' not found in state", repoName) + } + + // Double-check: get the current PID in the tmux pane to detect any running process + tmuxClient := tmux.NewClient() + currentPID, err := tmuxClient.GetPanePID(context.Background(), repo.TmuxSession, agent.TmuxWindow) + if err == nil && currentPID > 0 { + // Check if this PID is alive and different from what we checked above + if currentPID != agent.PID { + if process, err := os.FindProcess(currentPID); err == nil { + if err := process.Signal(syscall.Signal(0)); err == nil { + // There's a different running process in the pane + return fmt.Errorf("A process (PID %d) is already running in this tmux pane.\n\nTo restart:\n 1. Exit the current process first\n 2. Then run 'multiclaude claude' again\n\nOr attach to view:\n multiclaude attach %s", currentPID, agentName) + } + } + } + } + // Get the prompt file path (stored as ~/.multiclaude/prompts/.md) promptFile := filepath.Join(c.paths.Root, "prompts", agentName+".md")