{"record":{"id":"3eaa3430b496b450","repo":"chenhg5/cc-connect","slug":"pisession-write-get-state-probe-w","errorCode":null,"errorMessage":"piSession: write get_state probe: %w","messagePattern":"piSession: write get_state probe: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"agent/pi/session.go","lineNumber":234,"sourceCode":"\t// Pi's RPC protocol does not push a \"session\" event on stdout — the only\n\t// way to learn the session id is to send {\"type\":\"get_state\"} and parse\n\t// the matching response in handleEvent. We probe immediately after spawn\n\t// so that newPiSession's wait on rpcReady only unblocks once the id has\n\t// been stored. readLoopRPC closes rpcReady as soon as sessionIDReady()\n\t// flips to true (which happens after handleEvent processes the response),\n\t// so callers can safely read CurrentSessionID() the moment rpcReady fires.\n\t//\n\t// If the probe write fails, the session is unrecoverable: without the\n\t// session id we cannot resume after /stop, which is the very bug we are\n\t// fixing. Bail out immediately and let the caller surface the error\n\t// instead of waiting for the 30s rpcReady timeout.\n\tif err := s.writeRPCCommand(map[string]any{\n\t\t\"type\": \"get_state\",\n\t\t\"id\":   stateProbeID,\n\t}); err != nil {\n\t\tslog.Warn(\"piSession: failed to write get_state probe; aborting RPC start\", \"error\", err)\n\t\ts.killRPC()\n\t\treturn fmt.Errorf(\"piSession: write get_state probe: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (s *piSession) killRPC() {\n\tif s.rpcCmd != nil && s.rpcCmd.Process != nil {\n\t\tif err := forceKillCmd(s.rpcCmd); err != nil {\n\t\t\tslog.Warn(\"piSession: kill rpc process\", \"error\", err)\n\t\t}\n\t\t_, _ = s.rpcCmd.Process.Wait()\n\t}\n}\n\n// readLoopRPC is the persistent RPC readLoop goroutine.\n// One instance runs for the lifetime of the RPC process.\nfunc (s *piSession) readLoopRPC(stdout io.ReadCloser) {\n\tdefer s.wg.Done()","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/pi/session.go#L216-L252","documentation":"agent/pi/session.go:234 — after spawning the RPC child, startRPC writes a `{\"type\":\"get_state\"}` probe to the child's stdin to fetch the session id. If that write fails (usually because the pipe is broken because the child died or closed stdin immediately), the session is unrecoverable (no session id means /stop cannot resume), so startRPC kills the child and returns \"piSession: write get_state probe: %w\".","triggerScenarios":"Called from newPiSession in rpc mode when: (1) the pi process exits/crashes between cmd.Start() and the probe write; (2) pi rejects --mode rpc (older pi version without rpc support) and exits; (3) pi exits due to a bad --session-id / --model / --thinking argument; (4) the stdin pipe was closed due to a broken pipe after child death.","commonSituations":"pi CLI version too old to support `--mode rpc`; invalid flag combination (e.g. unknown --model or --thinking value) causing immediate exit; pi crashing on startup due to corrupt config or missing API key, closing stdin before the probe lands.","solutions":["Run `pi --mode rpc` manually with the same extra args and check its stderr for the immediate-exit cause; fix flags/values in config.","Upgrade the pi CLI to a version that supports `--mode rpc`.","Check that required pi credentials/env (e.g. provider API keys) are set in the daemon environment or extraEnv.","Retry session creation; if persistent, capture pi's stderr in daemon logs (slog.Warn already logs the probe error) for the underlying cause."],"exampleFix":"// before\nextraArgs = [\"--model\", \"gpt-nonexistent\"]  // pi exits instantly\n// after\nextraArgs = [\"--model\", \"claude-sonnet-4\"]","handlingStrategy":"try-catch","validationCode":"// Verify rpc support before starting a session:\nout, err := exec.Command(piCmd, \"--mode\", \"rpc\", \"--help\").CombinedOutput()\nif err != nil || !strings.Contains(string(out), \"rpc\") {\n    return fmt.Errorf(\"pi CLI lacks rpc mode support; upgrade pi\")\n}","typeGuard":null,"tryCatchPattern":"sess, err := agent.StartSession(ctx, sessionID, workDir)\nif err != nil {\n    if strings.Contains(err.Error(), \"write get_state probe\") {\n        slog.Error(\"pi RPC process died at startup; check pi stderr\", \"err\", err)\n    }\n    return err // session is unrecoverable; surface to user\n}","preventionTips":["Keep the pi CLI up to date (rpc mode requires a recent version).","Validate --model/--thinking/extraArgs values in config before deploying.","Ensure provider credentials are present in the daemon environment.","Test `pi --mode rpc` manually after any pi upgrade or config change."],"tags":["broken-pipe","rpc","process-crash","go","agent-pi"],"backgroundTag":"broken-pipe","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}