{"record":{"id":"660e725d989683d6","repo":"chenhg5/cc-connect","slug":"pi-context-cancelled-while-waiting-for-rpc-ready","errorCode":null,"errorMessage":"pi: context cancelled while waiting for rpc ready","messagePattern":"pi: context cancelled while waiting for rpc ready","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/pi/session.go","lineNumber":159,"sourceCode":"\t\ts.extPending = make(map[string]string)\n\t\ts.extPendingRev = make(map[string]string)\n\t\ts.extMethod = make(map[string]string)\n\n\t\tif err := s.startRPC(resumeID); err != nil {\n\t\t\tcancel()\n\t\t\treturn nil, fmt.Errorf(\"pi: start rpc: %w\", err)\n\t\t}\n\n\t\t// Wait for first JSON line (indicates RPC loop is live)\n\t\tselect {\n\t\tcase <-s.rpcReady:\n\t\tcase <-time.After(30 * time.Second):\n\t\t\ts.killRPC()\n\t\t\tcancel()\n\t\t\treturn nil, fmt.Errorf(\"pi: rpc process did not become ready within 30s\")\n\t\tcase <-ctx.Done():\n\t\t\ts.killRPC()\n\t\t\treturn nil, fmt.Errorf(\"pi: context cancelled while waiting for rpc ready\")\n\t\t}\n\t} else if resumeID != \"\" && resumeID != core.ContinueSession {\n\t\t// JSON mode: set the session ID directly since we don't spawn a process\n\t\t// that would emit a session event.\n\t\ts.sessionID.Store(resumeID)\n\t}\n\n\treturn s, nil\n}\n\n// ── RPC process helpers (rpc=true) ──────────────────────────\n\nfunc (s *piSession) startRPC(resumeID string) error {\n\targs := append(append([]string{}, s.extraArgs...), \"--mode\", \"rpc\")\n\tif resumeID != \"\" {\n\t\targs = append(args, \"--session-id\", resumeID)\n\t}\n\tif s.model != \"\" {","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/pi/session.go#L141-L177","documentation":"While waiting for the pi RPC subprocess's readiness signal (rpcReady), newPiSession also selects on ctx.Done(). If the caller's context is cancelled (or its deadline expires) before pi becomes ready, the subprocess is killed and this error is returned. It is the caller-driven cancellation counterpart to the fixed 30-second timeout.","triggerScenarios":"StartSession called with a context that is cancelled or expires while the pi RPC process is still starting: HTTP request context ended by the client disconnecting, platform shutdown cancelling the session context, an explicit deadline shorter than pi's startup time.","commonSituations":"Users cancelling a chat request while the session spins up; gateway timeouts with deadlines of a few seconds while pi takes 10-20s to start; engine shutdown/Stop() cancelling in-flight StartSession calls; tests passing already-cancelled contexts.","solutions":["Retry StartSession with a fresh, uncancelled context that has an adequate deadline (e.g. >30s to cover the probe).","Check what cancelled the context: inspect ctx.Err() (DeadlineExceeded vs Canceled) in the wrapping layer and address the upstream source.","If pi startup is routinely slow, raise the caller's deadline or keep a warm session pool so cancellation rarely interrupts startup.","Ensure the platform/engine isn't cancelling session contexts prematurely during reconnects."],"exampleFix":"// before\nsess, err := agent.StartSession(ctxWith2sDeadline, sessionID)\n// after\nctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)\ndefer cancel()\nsess, err := agent.StartSession(ctx, sessionID)","handlingStrategy":"try-catch","validationCode":"if err := ctx.Err(); err != nil {\n    return fmt.Errorf(\"context already done before starting pi session: %w\", err)\n}\n// prefer generous deadlines: startup + 30s probe\ndeadline, ok := ctx.Deadline()\nif ok && time.Until(deadline) < 35*time.Second {\n    log.Printf(\"warning: %s may be too short for pi RPC startup\", deadline)\n}","typeGuard":null,"tryCatchPattern":"sess, err := agent.StartSession(ctx, opts)\nif err != nil {\n    if errors.Is(ctx.Err(), context.Canceled) {\n        return fmt.Errorf(\"session start cancelled by caller: %w\", err)\n    }\n    if errors.Is(ctx.Err(), context.DeadlineExceeded) {\n        // retry with a fresh, longer-deadline context\n        sess, err = agent.StartSession(freshCtx, opts)\n    }\n}","preventionTips":["Give session-start calls deadlines comfortably above 30s.","Distinguish context.Canceled (caller gave up) from DeadlineExceeded (retry candidate).","Avoid binding session startup to short-lived HTTP request contexts.","Keep sessions warm so cancellation rarely lands during startup."],"tags":["go","context","cancellation","rpc"],"backgroundTag":"context-cancelled","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}