{"record":{"id":"71bb25fb61e13619","repo":"Hmbown/CodeWhale","slug":"persistent-service-has-no-process-group-id","errorCode":null,"errorMessage":"Persistent service has no process group id","messagePattern":"Persistent service has no process group id","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/shell.rs","lineNumber":2610,"sourceCode":"            stdin,\n            child: Some(child),\n            #[cfg(windows)]\n            windows_job,\n            stdout_thread,\n            stderr_thread,\n            work_lifecycle,\n            lifecycle_seq: 0,\n            last_lifecycle_status: None,\n            last_lifecycle_bytes: 0,\n        };\n\n        #[cfg(unix)]\n        if persist_pending {\n            let process_group_id = bg_shell\n                .child\n                .as_ref()\n                .and_then(ShellChild::process_id)\n                .ok_or_else(|| anyhow!(\"Persistent service has no process group id\"))?;\n            register_pending_persistent_process_group(process_group_id);\n        }\n\n        if let Some(input) = stdin_data\n            && let Err(err) = bg_shell.write_stdin(input, false)\n        {\n            let _ = bg_shell.kill();\n            return Err(err);\n        }\n\n        if let Err(err) = bg_shell.publish_lifecycle() {\n            let _ = bg_shell.kill();\n            return Err(err);\n        }\n\n        self.processes.insert(task_id.clone(), bg_shell);\n        spawn_guard.disarm();\n        // Evict here, not only from `list_jobs()`: retention must not depend on","sourceCodeStart":2592,"sourceCodeEnd":2628,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/shell.rs#L2592-L2628","documentation":"For background shells spawned with persist_pending on Unix, the child must expose a process id so its process group can be registered (register_pending_persistent_process_group) for later lifecycle management/cleanup. If the child handle yields no pid — the process exited and was reaped before registration, or the spawn variant does not track a pid — registration fails with this error before stdin/lifecycle publishing.","triggerScenarios":"Registering a persistent service whose process dies instantly (bad executable path, immediate exit) so the pid is already gone; or a child wrapper on this path lacking process-id tracking.","commonSituations":"persist: true service pointing at a nonexistent binary or one that exits immediately; race between spawn and pid registration for very short-lived 'services'.","solutions":["Verify the command runs and stays alive when launched manually.","Check the executable path/arguments for the persistent service.","If the service legitimately exits quickly, run it as a normal (non-persistent) background or sync command instead of persist_pending."],"exampleFix":"# before\nexec_shell(\"./missing-binary --serve\", { background: true, persist: true })\n\n# after\nexec_shell(\"./existing-binary --serve\", { background: true, persist: true })","handlingStrategy":"retry","validationCode":"// Pre-check the service binary exists and is executable before persist-spawn.\nlet program = command.split_whitespace().next().unwrap_or_default();\nlet resolved = resolve_in(workspace, program);\nanyhow::ensure!(\n    resolved.map(|p| p.is_file()).unwrap_or(false),\n    \"persistent service program '{program}' not found in workspace\"\n);","typeGuard":null,"tryCatchPattern":"match spawn_persistent(command).await {\n    Ok(task) => task,\n    Err(e) if e.to_string().contains(\"no process group id\") => {\n        // Process died before registration: verify the command, then retry once.\n        verify_program_exists(&command)?;\n        spawn_persistent(command).await?\n    }\n    Err(e) => return Err(e),\n};","preventionTips":["Only mark a command persist_pending if it is a long-lived service; short-lived commands belong in normal background/sync mode.","Verify the executable path resolves inside the workspace before spawning.","Smoke-test the service command in the foreground once before promoting it to a persistent service."],"tags":["shell","persistent-service","process-group","unix","spawn"],"backgroundTag":"process-exited-immediately","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}