{"record":{"id":"3a453cc5ac563dbe","repo":"chenhg5/cc-connect","slug":"ping-timeout","errorCode":null,"errorMessage":"ping timeout","messagePattern":"ping timeout","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/copilot/session.go","lineNumber":169,"sourceCode":"\tif err := cs.handshake(resumeSessionID); err != nil {\n\t\t_ = cs.Close()\n\t\treturn nil, fmt.Errorf(\"copilotSession: handshake failed: %w\", err)\n\t}\n\n\treturn cs, nil\n}\n\nfunc (cs *copilotSession) handshake(resumeSessionID string) error {\n\t// Step 1: Ping\n\t_, pingCh := cs.rpc.call(\"ping\", nil)\n\tselect {\n\tcase resp := <-pingCh:\n\t\tif resp.Error != nil {\n\t\t\treturn fmt.Errorf(\"ping: %w\", resp.Error)\n\t\t}\n\t\tslog.Debug(\"copilotSession: ping OK\")\n\tcase <-time.After(10 * time.Second):\n\t\treturn fmt.Errorf(\"ping timeout\")\n\tcase <-cs.ctx.Done():\n\t\treturn cs.ctx.Err()\n\t}\n\n\t// Step 2: Create or resume session\n\tif resumeSessionID != \"\" && resumeSessionID != core.ContinueSession {\n\t\t_, resumeCh := cs.rpc.call(\"session.resume\", cs.sessionConfig(resumeSessionID))\n\t\tselect {\n\t\tcase resp := <-resumeCh:\n\t\t\tif resp.Error != nil {\n\t\t\t\tslog.Warn(\"copilotSession: resume failed, creating new session\", \"error\", resp.Error)\n\t\t\t\treturn cs.createSession()\n\t\t\t}\n\t\t\tcs.sessionID.Store(resumeSessionID)\n\t\t\tslog.Info(\"copilotSession: session resumed\", \"sessionId\", resumeSessionID)\n\t\tcase <-time.After(10 * time.Second):\n\t\t\treturn fmt.Errorf(\"session.resume timeout\")\n\t\tcase <-cs.ctx.Done():","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/copilot/session.go#L151-L187","documentation":"Handshake step 1 waits up to 10 seconds for the `ping` response. If the timer fires first (no reply, no context cancellation), handshake returns the literal error \"ping timeout\". It means the copilot process is alive or hung but its JSON-RPC loop is not answering.","triggerScenarios":"handshake() from newCopilotSession: no ping response arrives on pingCh within time.After(10*time.Second) — reader goroutine stuck, child process hung, output not LSP-framed so the reader never completes a message, or the CLI is blocked on auth/network.","commonSituations":"Copilot CLI waiting for interactive login on stdout/stdin; child stuck due to a full stderr/stdout pipe; a very slow cold start exceeding 10s; output framing mismatch so readLoop can't parse any message; CI machines with heavy startup latency.","solutions":["Check the copilot CLI stderr buffer for interactive prompts (login prompts hang the handshake)","Run the CLI manually to confirm it starts non-interactively and answers requests","If cold start is slow in your environment, this timeout is the guard — pre-warm the agent or accept the failure and retry","Verify stdout is pure LSP-framed JSON-RPC (a framing bug makes every read hang) — see readMessage errors","Increase the 10s timeout in session.go only after confirming the child is genuinely slow, not hung"],"exampleFix":"// before: CLI launches interactively and hangs\nchild := exec.Command(binPath)  // prompts \"sign in?\" on first run\n// after: pre-authenticate so startup never blocks on stdin\n// run once as the daemon user: copilot auth login\nchild := exec.Command(binPath, \"--headless\") // or equivalent non-interactive flag","handlingStrategy":"retry","validationCode":"// ensure the CLI can start and respond non-interactively before session creation\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\nout, err := exec.CommandContext(ctx, copilotPath, \"--version\").Output()\ncancel()\nif err != nil || len(bytes.TrimSpace(out)) == 0 {\n    return fmt.Errorf(\"copilot CLI unresponsive/interactive: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"sess, err := StartSession(ctx, cfg, resumeID)\nif err != nil {\n    if strings.Contains(err.Error(), \"ping timeout\") {\n        // one retry; repeated timeouts mean the CLI hangs and needs diagnosis\n        if attempt++; attempt < 2 {\n            return StartSession(ctx, cfg, \"\")\n        }\n        return fmt.Errorf(\"copilot ping timeout after retry: %w\", err)\n    }\n    return err\n}","preventionTips":["Guarantee non-interactive startup (pre-auth, no prompts) for the daemon user","Ensure stdout stays pure JSON-RPC — stderr is where logs belong","Keep stderr drained (reader goroutine) so the child never blocks on a full pipe","Raise the 10s timeout only on hosts with proven slow cold starts; investigate hangs instead"],"tags":["timeout","handshake","jsonrpc","copilot"],"backgroundTag":"request-timeout","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"}