{"record":{"id":"13f412cd5fb5c53c","repo":"atuinsh/atuin","slug":"process-with-parent-pid-does-not-exist","errorCode":null,"errorMessage":"Process with parent pid does not exist","messagePattern":"Process with parent pid does not exist","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/atuin-common/src/shell.rs","lineNumber":49,"sourceCode":"    #[error(\"shell not supported\")]\n    NotSupported,\n\n    #[error(\"failed to execute shell command: {0}\")]\n    ExecError(String),\n}\n\nimpl Shell {\n    #[must_use]\n    pub fn current() -> Self {\n        let sys = System::new_all();\n\n        let process = sys\n            .process(get_current_pid().expect(\"Failed to get current PID\"))\n            .expect(\"Process with current pid does not exist\");\n\n        let parent = sys\n            .process(process.parent().expect(\"Atuin running with no parent!\"))\n            .expect(\"Process with parent pid does not exist\");\n\n        let shell = parent.name().to_string_lossy().trim().to_lowercase();\n        let shell = shell.strip_prefix('-').unwrap_or(&shell);\n\n        Self::from_string(shell)\n    }\n\n    #[must_use]\n    pub fn from_env() -> Self {\n        std::env::var(\"ATUIN_SHELL\")\n            .map_or(Self::Unknown, |shell| Self::from_string(&shell.trim().to_lowercase()))\n    }\n\n    #[must_use]\n    pub fn config_file(&self) -> Option<std::path::PathBuf> {\n        let mut path = directories::BaseDirs::new()?.home_dir().to_owned();\n\n        // TODO: handle all shells","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/atuinsh/atuin/blob/c0c717ab04c881764bcad4b3d169a507e2432643/crates/atuin-common/src/shell.rs#L31-L67","documentation":"After obtaining the parent PID, Shell::current looks it up in the sysinfo process table. If the parent process has already exited between PID extraction and the lookup, sys.process(...) returns None and the expect panics with 'Process with parent pid does not exist'.","triggerScenarios":"Calling Shell::current when the parent shell has exited (or its PID was recycled) before System::new_all() captured it — a classic TOCTOU race in fast hook invocations.","commonSituations":"Shell precmd hooks racing with shell exit; atuin invoked via `exec` chains where the original parent is gone; short-lived wrapper scripts that exit immediately after spawning atuin.","solutions":["Retry the detection — the parent may be present on a fresh snapshot.","Fall back to the $SHELL environment variable when the parent lookup fails.","Pass an explicit parent PID/name (shell_name's parent parameter) from the shell hook instead of relying on live process inspection.","Check for PID recycling issues if many processes churn rapidly."],"exampleFix":"// before\nlet parent = sys.process(process.parent().expect(\"Atuin running with no parent!\"))\n    .expect(\"Process with parent pid does not exist\");\n\n// after\nlet shell_name = process\n    .parent()\n    .and_then(|ppid| sys.process(ppid))\n    .map(|p| p.name().to_string_lossy().to_lowercase())\n    .or_else(|| std::env::var(\"SHELL\").ok())\n    .unwrap_or_default();","handlingStrategy":"fallback","validationCode":"// Rust: verify parent is still alive before deriving shell from it\nlet mut sys = sysinfo::System::new_all();\nlet parent_alive = sysinfo::get_current_pid().ok()\n    .and_then(|pid| sys.process(pid))\n    .and_then(|p| p.parent())\n    .and_then(|ppid| sys.process(ppid))\n    .is_some();\nif !parent_alive { /* use $SHELL fallback */ }","typeGuard":null,"tryCatchPattern":"// Rust\nlet shell = std::panic::catch_unwind(Shell::current)\n    .ok()\n    .or_else(|| std::env::var(\"SHELL\").ok().map(|s| Shell::from_string(&s)));","preventionTips":["Pass an explicit parent PID/name from shell hooks instead of live lookup.","Retry detection if the wrapper process may still be exiting.","Fall back to $SHELL when the parent has exited.","Avoid wrapper scripts that spawn atuin and immediately exit."],"tags":["process","panic","race-condition","shell-detection"],"backgroundTag":"resource-not-found","analyzedSha":"c0c717ab04c881764bcad4b3d169a507e2432643","analyzedAt":"2026-09-12T07:40:01.341Z","contentChangedAt":"2026-09-12T07:40:01.341Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}