{"record":{"id":"bb3550be0f480154","repo":"chenhg5/cc-connect","slug":"iflowsession-start-pty-w","errorCode":null,"errorMessage":"iflowSession: start pty: %w","messagePattern":"iflowSession: start pty: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/iflow/session.go","lineNumber":201,"sourceCode":"\t} else if s.sentOnce.Load() {\n\t\targs = append(args, \"-c\")\n\t}\n\n\targs = append(args, \"-i\", prompt)\n\tslog.Debug(\"iflowSession: launching interactive turn\", \"resume\", sid != \"\", \"args\", core.RedactArgs(args))\n\n\tcmd := exec.CommandContext(turnCtx, s.cmd, args...)\n\tcmd.Dir = s.workDir\n\tenv := os.Environ()\n\tif len(s.extraEnv) > 0 {\n\t\tenv = core.MergeEnv(env, s.extraEnv)\n\t}\n\tcmd.Env = env\n\n\tptmx, err := pty.Start(cmd)\n\tif err != nil {\n\t\ts.turnActive.Store(false)\n\t\treturn fmt.Errorf(\"iflowSession: start pty: %w\", err)\n\t}\n\n\ts.sentOnce.Store(true)\n\ts.wg.Add(1)\n\tgo s.readLoop(turn, cmd, ptmx)\n\treturn nil\n}\n\nfunc (s *iflowSession) readLoop(turn *iflowTurn, cmd *exec.Cmd, ptmx *os.File) {\n\tdefer s.wg.Done()\n\tdefer s.turnActive.Store(false)\n\tdefer turn.cancel()\n\tdefer ptmx.Close()\n\n\tvar termBuf bytes.Buffer\n\tdrainDone := make(chan struct{})\n\tgo func() {\n\t\t_, _ = io.Copy(&termBuf, ptmx)","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/iflow/session.go#L183-L219","documentation":"Wraps a failed pty.Start in iflowSession.Send: the interactive iflow turn must run under a pseudo-terminal, and the OS refused to allocate one or exec the process. The turnActive flag is reset so the session is not stuck; causes include missing /dev/ptmx, fd limits, or a dead binary.","triggerScenarios":"Calling Send() when pty.Start fails — the iflow binary vanished from PATH after New(), the resolved command path is not executable, argument list is invalid, or PTY allocation failed (resource limits, non-tty-capable environment).","commonSituations":"CLI uninstalled/upgraded between agent construction and session use; sandbox/container without /dev/ptmx; too many open files; binary lacks execute permission after a partial update.","solutions":["Verify the iflow CLI is still present and executable: `which iflow && iflow --version`","Check file-descriptor/process limits (ulimit -n) if PTY allocation fails","Ensure the container/runtime exposes /dev/pts (run with a tty-capable setup)","Recreate the agent/session so exec.LookPath re-resolves the binary"],"exampleFix":"// before\ncmd := exec.Command(\"iflow\", args...) // stale binary path\n// after\nbin, err := exec.LookPath(\"iflow\")\nif err != nil { return fmt.Errorf(\"iflow CLI missing: %w\", err) }\ncmd := exec.Command(bin, args...)","handlingStrategy":"try-catch","validationCode":"bin, err := exec.LookPath(\"iflow\")\nif err != nil || unix.Access(bin, unix.X_OK) != nil {\n    return fmt.Errorf(\"iflow binary missing or not executable\")\n}","typeGuard":null,"tryCatchPattern":"if err := session.Send(ctx, msg, nil); err != nil {\n    if strings.Contains(err.Error(), \"start pty\") {\n        log.Printf(\"pty start failed, check fd limits and /dev/pts: %v\", err)\n        return recreateSessionAndRetry()\n    }\n}","preventionTips":["Run health checks (DoctorChecker) that verify the CLI is executable before sending","Raise RLIMIT_NOFILE in containers running many sessions","Ensure the runtime provides /dev/pts for PTY allocation"],"tags":["go","pty","process","iflow"],"backgroundTag":"command-not-found","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"}