{"record":{"id":"7d223f47f2540c2a","repo":"Hmbown/CodeWhale","slug":"pty-job-is-no-longer-running","errorCode":null,"errorMessage":"PTY job is no longer running","messagePattern":"PTY job is no longer running","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/tui/src/tools/shell.rs","lineNumber":2918,"sourceCode":"        shell.write_stdin_bytes(input, close)?;\n        Ok(())\n    }\n\n    /// Historical evicted records have no terminal-size evidence.\n    pub fn job_terminal_size(&self, task_id: &str) -> Option<PtyDimensions> {\n        self.processes\n            .get(task_id)\n            .and_then(|shell| shell.terminal_size)\n    }\n\n    pub fn resize_pty(&mut self, task_id: &str, size: PtyDimensions) -> Result<()> {\n        let size = size.validate()?;\n        let shell = self\n            .processes\n            .get_mut(task_id)\n            .ok_or_else(|| anyhow!(\"Job {task_id} not found\"))?;\n        shell.poll();\n        anyhow::ensure!(\n            shell.status == ShellStatus::Running,\n            \"PTY job is no longer running\"\n        );\n        #[cfg(not(target_env = \"ohos\"))]\n        {\n            let master = shell.pty_master.as_ref().context(\"Job is not a PTY\")?;\n            master\n                .resize(PtySize {\n                    rows: size.rows,\n                    cols: size.cols,\n                    pixel_width: 0,\n                    pixel_height: 0,\n                })\n                .context(\"Failed to resize PTY\")?;\n            shell.terminal_size = Some(size);\n            Ok(())\n        }\n        #[cfg(target_env = \"ohos\")]","sourceCodeStart":2900,"sourceCodeEnd":2936,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tools/shell.rs#L2900-L2936","documentation":"After resolving the job, resize_pty polls it and requires ShellStatus::Running before touching the PTY master; a finished (exited, failed, killed, or evicted-pending) job fails with 'PTY job is no longer running'. Resizing a dead pseudo-terminal would either error in portability internals or silently do nothing, so the manager rejects it with this explicit state error.","triggerScenarios":"Calling resize_pty on a tracked job whose process has already exited — e.g. the job finished between spawn and the resize call, was killed, or the resize event arrives after process exit.","commonSituations":"Terminal SIGWINCH-style resize delivered just after a long-running command finished; UI resizing a panel bound to a completed PTY job; race between job exit notification and a queued resize request.","solutions":["Check job status (via get_output/poll_status or the job list) and skip the resize when it is not Running.","Drop queued resize events when the job reaches a terminal status.","If the job should still be alive, inspect its exit code/stderr to see why it exited before attempting further interaction.","Re-run the command with a fresh task_id if an interactive PTY session is still needed."],"exampleFix":"// before\nmanager.resize_pty(task_id, size)?;\n// after\nmatch manager.poll_status(task_id) {\n    Ok(s) if s == ShellStatus::Running => manager.resize_pty(task_id, size)?,\n    Ok(s) => eprintln!(\"job {task_id} is {s:?}; skipping resize\"),\n    Err(_) => eprintln!(\"job {task_id} untracked; skipping resize\"),\n}","handlingStrategy":"validation","validationCode":"// Rust: gate the resize on live Running status\nlet status = manager.poll_status(task_id)?; // errors if untracked\nif status == ShellStatus::Running {\n    manager.resize_pty(task_id, size)?;\n} else {\n    eprintln!(\"job {task_id} is {status:?}; skipping PTY resize\");\n}","typeGuard":"fn is_running(status: &ShellStatus) -> bool { *status == ShellStatus::Running }","tryCatchPattern":"match manager.resize_pty(task_id, size) {\n    Ok(()) => (),\n    Err(e) if e.to_string().contains(\"no longer running\") => {\n        // read final output/exit code instead of resizing\n        let final_out = manager.get_output(task_id, false, 0)?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Subscribe to job completion events and cancel pending resize requests on terminal status","Check poll_status/get_output before resizing, especially after long waits","Never resize a PTY job after observing a non-Running status — the check is not recoverable","Treat this as a benign race in UI code: log and skip rather than failing the whole interaction"],"tags":["pty","resize","process-exited","state"],"backgroundTag":"invalid-state-transition","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}