{"record":{"id":"3343ee308a55a093","repo":"chenhg5/cc-connect","slug":"copilot-probe-stdout-pipe-w","errorCode":null,"errorMessage":"copilot probe: stdout pipe: %w","messagePattern":"copilot probe: stdout pipe: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/copilot/copilot.go","lineNumber":223,"sourceCode":"\n// newProbeSession spawns a copilot --headless --stdio probe process, starts a\n// read loop, and returns a probeSession. Caller must call close() when done.\nfunc newProbeSession(ctx context.Context, snapshot probeSnapshot) (*probeSession, error) {\n\tprobeCtx, cancel := context.WithCancel(ctx)\n\n\tcmd := exec.CommandContext(probeCtx, snapshot.cmd, \"--headless\", \"--stdio\", \"--no-auto-update\")\n\tcmd.Dir = snapshot.workDir\n\tcmd.Env = snapshot.env\n\n\tstdin, err := cmd.StdinPipe()\n\tif err != nil {\n\t\tcancel()\n\t\treturn nil, fmt.Errorf(\"copilot probe: stdin pipe: %w\", err)\n\t}\n\tstdout, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\tcancel()\n\t\treturn nil, fmt.Errorf(\"copilot probe: stdout pipe: %w\", err)\n\t}\n\tcmd.Stderr = io.Discard\n\n\tif err := cmd.Start(); err != nil {\n\t\tcancel()\n\t\treturn nil, fmt.Errorf(\"copilot probe: start: %w\", err)\n\t}\n\n\trpc := newRPCClient(stdin)\n\treader := newLSPReader(stdout)\n\tdone := make(chan struct{})\n\n\tgo func() {\n\t\tdefer func() {\n\t\t\t_ = stdin.Close()\n\t\t\t_ = cmd.Wait()\n\t\t\tclose(done)\n\t\t}()","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/copilot/copilot.go#L205-L241","documentation":"This error wraps the failure to create an stdout pipe on the `copilot` CLI process that the probe session spawns to list/delete sessions via LSP-style JSON-RPC. It is thrown by newProbeSession when cmd.StdoutPipe() returns an error, before the process is started. It almost always indicates an OS-level resource problem (too many open files, exhausted pipes) rather than a copilot configuration issue.","triggerScenarios":"Calling ListSessions or DeleteSession when the os/exec StdoutPipe call fails, typically because the process has exhausted its file-descriptor limit (EMFILE) or the pipe syscall fails.","commonSituations":"Long-running cc-connect daemons leaking copilot probe processes and hitting the ulimit -n / RLIMIT_NOFILE ceiling; containers with low fd limits; running under a heavily loaded system where pipe allocation fails.","solutions":["Check for leaked copilot processes / probe sessions not being closed (LookPath/spawn leaks) and fix the leak","Raise the file-descriptor limit (ulimit -n) for the cc-connect daemon process","In containers, raise RLIMIT_NOFILE (e.g. docker --ulimit nofile=65536:65536)","Retry the ListSessions/DeleteSession call once resources are freed"],"exampleFix":"// before\ncmd := exec.CommandContext(ctx, bin, args...)\nstdout, err := cmd.StdoutPipe() // fails under fd exhaustion\n// after\n// ensure previous probe cmd.Wait() is always called via defer so pipes close\nfunc (a *Agent) newProbeSession(ctx context.Context) (*probeSession, error) {\n    ctx, cancel := context.WithTimeout(ctx, probeTimeout)\n    defer func() { ... }()\n    cmd := exec.CommandContext(ctx, bin, args...)\n    stdout, err := cmd.StdoutPipe()\n    if err != nil { cancel(); return nil, fmt.Errorf(\"copilot probe: stdout pipe: %w\", err) }\n    ...\n}","handlingStrategy":"retry","validationCode":"// before calling ListSessions/DeleteSession\nfunc fdsAvailable(minFree uint64) bool {\n    entries, err := os.ReadDir(\"/proc/self/fd\")\n    if err != nil { return true }\n    var rlim syscall.Rlimit\n    syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim)\n    return uint64(len(entries))+minFree < rlim.Cur\n}","typeGuard":null,"tryCatchPattern":"err := a.ListSessions(ctx)\nif err != nil && strings.Contains(err.Error(), \"stdout pipe\") {\n    slog.Warn(\"fd exhaustion suspected; retrying after GC/close\", \"err\", err)\n    runtime.GC()\n    err = a.ListSessions(ctx)\n}","preventionTips":["Always Wait() probe commands so pipes/fds are released","Monitor open fd counts for the daemon in production","Set generous RLIMIT_NOFILE for the cc-connect service unit (LimitNOFILE=65536)"],"tags":["go","process","file-descriptors","copilot"],"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-14T00:17:10.932Z"}