{"record":{"id":"db0348c5ab9aa96c","repo":"chenhg5/cc-connect","slug":"copilotsession-stdin-pipe-w","errorCode":null,"errorMessage":"copilotSession: stdin pipe: %w","messagePattern":"copilotSession: stdin pipe: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/copilot/session.go","lineNumber":109,"sourceCode":"\n\targs := append(append([]string{}, extraArgs...), \"--headless\", \"--stdio\", \"--no-auto-update\")\n\n\tslog.Debug(\"copilotSession: starting\", \"bin\", cliBin, \"args\", args, \"dir\", workDir)\n\n\tchild := exec.CommandContext(sessionCtx, cliBin, args...)\n\tchild.Dir = workDir\n\tprepareCmdForKill(child)\n\n\tenv := os.Environ()\n\tif len(extraEnv) > 0 {\n\t\tenv = core.MergeEnv(env, extraEnv)\n\t}\n\tchild.Env = env\n\n\tstdin, err := child.StdinPipe()\n\tif err != nil {\n\t\tcancel()\n\t\treturn nil, fmt.Errorf(\"copilotSession: stdin pipe: %w\", err)\n\t}\n\n\tstdout, err := child.StdoutPipe()\n\tif err != nil {\n\t\tcancel()\n\t\treturn nil, fmt.Errorf(\"copilotSession: stdout pipe: %w\", err)\n\t}\n\n\tvar stderrBuf bytes.Buffer\n\tchild.Stderr = &stderrBuf\n\n\tif err := child.Start(); err != nil {\n\t\tcancel()\n\t\treturn nil, fmt.Errorf(\"copilotSession: start: %w\", err)\n\t}\n\n\tcs := &copilotSession{\n\t\tcmd:                child,","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/copilot/session.go#L91-L127","documentation":"newCopilotSession builds the exec.Command for the copilot CLI and obtains an stdin pipe so the RPC client can write JSON-RPC requests. If child.StdinPipe() returns an error the context is cancelled and construction aborts with \"copilotSession: stdin pipe: %w\". This means the OS pipe could not be created and the session can never be started.","triggerScenarios":"StartSession → newCopilotSession where StdinPipe() fails — practically only under fd exhaustion, a closed/invalid os.Pipe environment, or an exec.Cmd in an invalid state (already started, or Wait called previously).","commonSituations":"Process file-descriptor exhaustion (ulimit, leaked pipes in a long-running daemon); reusing a single exec.Cmd for multiple sessions; running in a container with a tiny fd limit.","solutions":["Check for fd leaks — run `lsof -p <pid>` and raise the limit (ulimit -n) in the daemon environment","Ensure each session creates a fresh exec.Cmd; never reuse one across sessions","Restart the daemon if the fd table is exhausted; the error is usually transient at the OS level","If it persists in containers, raise the nofile limit in the container runtime config"],"exampleFix":"// before: reusing one exec.Cmd per request\ncmd := sharedCmd\ns, err := newCopilotSession(ctx, cmd, ...)\n// after: fresh command per session\ns, err := newCopilotSession(ctx, exec.CommandContext(ctx, binPath, args...), ...)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"sess, err := StartSession(ctx, cfg, resumeID)\nif err != nil {\n    if strings.Contains(err.Error(), \"stdin pipe\") {\n        slog.Error(\"copilot: cannot create stdin pipe (fd exhaustion?)\", \"err\", err)\n    }\n    return fmt.Errorf(\"start session: %w\", err)\n}","preventionTips":["Monitor open fd count of the daemon; alert before hitting ulimit","Always Close()/Wait sessions promptly to release pipes","Create a new exec.Cmd per session; never reuse one","Raise LimitNOFILE for systemd/launchd services"],"tags":["process","pipe","stdin","exec"],"backgroundTag":"file-open-failed","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"}