From 8f9708c92f79c2de73b141865606d65b4c747b1a Mon Sep 17 00:00:00 2001 From: David Aronchick Date: Tue, 27 Jan 2026 00:19:11 +0000 Subject: [PATCH 1/2] 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 3e06628..f4bec25 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" @@ -5493,6 +5494,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 @@ -5516,6 +5518,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") From b50f7232f25c41e8534747ad332bdd6f57cd0cf9 Mon Sep 17 00:00:00 2001 From: David Aronchick Date: Thu, 29 Jan 2026 19:15:01 +0000 Subject: [PATCH 2/2] fix: lowercase error messages to satisfy staticcheck ST1005 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed two error messages to start with lowercase to comply with Go's error formatting conventions (staticcheck rule ST1005): - "Claude is already running..." → "claude is already running..." - "A process..." → "a process..." These are multi-line user-facing error messages that remain helpful while following Go's convention that error strings should not be capitalized (since they may be wrapped in other error contexts). Fixes lint failures in PR #321 Co-Authored-By: Claude Sonnet 4.5 --- internal/cli/cli.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index f4bec25..9bb6576 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -5527,7 +5527,7 @@ func (c *CLI) restartClaude(args []string) error { 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) + 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) } } } @@ -5547,7 +5547,7 @@ func (c *CLI) restartClaude(args []string) error { 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) + 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) } } }