Skip to content

Stopping a hosted Pi agent reports it as failed to start #845

Description

@realtonyyoung

Symptom

Stopping a hosted Pi agent (from the web UI or the desktop app) leaves it showing "Agent failed to start" in the web UI, even when the session ran for minutes and has a transcript.

Seen 2026-09-09 with kcap 0.11.40 / Pi 0.85.1: two hosted Pi agents stopped by their owner 6–10 minutes after launch, both logged as exited with code 137, both shown as failed. Claude and Codex agents stopped the same way exit 0 and report Completed.

Root cause

Three pieces, each in the daemon:

  1. The stop is a SIGKILL. PiRpcHostedAgentRuntime.RequestGracefulStopAsync sends Pi an abort RPC — which ends the current turn, not the process — waits the 3 s stop grace, then calls TerminateAsync. IPiRpcProcess.TerminateAsync is Process.Kill(entireProcessTree: true), so the child never gets a chance to exit cleanly and the exit code is always 137.
  2. Non-zero exit means Failed. The orchestrator's exit handling maps exitCode != 0 to Failed and reports AgentStatusChanged(id, "Failed", agent.SessionId). An owner-requested stop is not distinguished from a crash.
  3. The status report carries no session id for non-PTY runtimes. agent.SessionId is only ever set by the PTY transcript-file match; ACP/RPC runtimes expose the canonical id through IAcpTranscriptSource.AcpSessionId, which SnapshotAgentsForStatus already falls back to but the server-facing AgentStatusChanged calls do not. The server therefore receives a failure with a null session id.

The web UI's AgentDetailView renders "Agent failed to start" exactly when the registry status is failed and the session id is null.

Fix direction

  • Treat an owner-requested stop as a stop: when the stop path initiated the termination, report Completed (or a dedicated stopped status) regardless of the kill's exit code, the way PendingEndReason already attributes other teardowns.
  • Give Pi a chance to exit cleanly (a real quit, or closing stdin) before falling back to Kill, so 137 stops being the normal outcome.
  • Report the canonical session id for non-PTY runtimes on every AgentStatusChanged, falling back to IAcpTranscriptSource.AcpSessionId as the status snapshot does.

Related: #839.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions