{"record":{"id":"9d088ec5d13ce46d","repo":"Hmbown/CodeWhale","slug":"inline-stderr-logger-panicked","errorCode":null,"errorMessage":"inline stderr logger panicked","messagePattern":"inline stderr logger panicked","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/lane/src/runtime.rs","lineNumber":1021,"sourceCode":"        let stderr = child\n            .stderr\n            .take()\n            .context(\"inline lane child stderr was not piped\")?;\n        let stdout_log = record.log_path.clone();\n        let stderr_log = record.log_path.clone();\n        let stdout_thread =\n            thread::spawn(move || stream_child_output(stdout, stdout_log, \"stdout\"));\n        let stderr_thread =\n            thread::spawn(move || stream_child_output(stderr, stderr_log, \"stderr\"));\n        let status = child\n            .wait()\n            .with_context(|| format!(\"wait for inline command {:?}\", spec.command))?;\n        let stdout_result = stdout_thread\n            .join()\n            .map_err(|_| anyhow::anyhow!(\"inline stdout logger panicked\"))?;\n        let stderr_result = stderr_thread\n            .join()\n            .map_err(|_| anyhow::anyhow!(\"inline stderr logger panicked\"))?;\n        let logging_error = stdout_result.err().or_else(|| stderr_result.err());\n\n        if status.success() && logging_error.is_none() {\n            append_log_event(\n                &record.log_path,\n                serde_json::json!({\"type\": \"lane_completed\", \"lane_id\": record.id}),\n            )?;\n            let _ = registry.mark_terminal_if_active(record, LaneStatus::Completed)?;\n        } else {\n            append_log_event(\n                &record.log_path,\n                serde_json::json!({\n                    \"type\": \"lane_failed\",\n                    \"lane_id\": record.id,\n                    \"status\": format!(\"{status}\"),\n                    \"logging_error\": logging_error.as_ref().map(ToString::to_string),\n                }),\n            )?;","sourceCodeStart":1003,"sourceCodeEnd":1039,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/lane/src/runtime.rs#L1003-L1039","documentation":"The stderr counterpart of the inline `start` logger panic: after the inline command exits, joining the stderr pump thread yields Err because the thread panicked, and this error is raised. The child's stderr may be missing from the lane log and lane completion (`lane_completed` event) is not recorded when logging failed.","triggerScenarios":"Inline lane `start` where the stderr thread (`stream_child_output(stderr, stderr_log, \"stderr\")`) panics — most commonly an unwrap on a failed append to `record.log_path` triggered by the command writing to stderr at the wrong moment (e.g. after cleanup started).","commonSituations":"Log handle invalidated mid-run (directory removed, permissions, disk full); panics inside `stream_child_output` on serialization of unexpected stderr content; stop/attach flows closing resources while the logger thread still writes.","solutions":["Locate the panic site from the thread's printed panic message and fix the underlying I/O condition in `stream_child_output`.","Keep `record.log_path` and its directory present and writable until all logger threads have joined.","Check disk space and fd limits for long-running or chatty commands.","Refactor `stream_child_output` to return Result instead of unwrapping so join() surfaces the true error and it can be recorded via `append_log_event`.","Re-run the lane and confirm the `lane_completed` event lands in the log after a clean run."],"exampleFix":"// before\nfile.write_all(chunk).expect(\"stderr logger\");\n// after\nfile.write_all(chunk).context(\"stderr log append\")?;","handlingStrategy":"try-catch","validationCode":"let Ok(meta) = std::fs::metadata(&record.log_path) else { /* create it */ };\nassert!(!meta.permissions().readonly(), \"lane log is read-only\");","typeGuard":"fn is_panic_join<T>(r: &std::thread::Result<T>) -> bool { r.is_err() }","tryCatchPattern":"match lane_start_inline(spec) {\n    Err(e) if e.to_string().contains(\"inline stderr logger panicked\") => {\n        eprintln!(\"stderr logger thread panicked: {e:#}; lane_completed not recorded, inspect log dir\");\n    }\n    Err(e) => eprintln!(\"start failed: {e:#}\"),\n    Ok(record) => { /* normal path */ }\n}","preventionTips":["Guarantee the log file outlives all pump threads: join threads before any cleanup.","Handle write errors explicitly in `stream_child_output` instead of expect()/unwrap().","Cap stderr volume or rotate logs to avoid disk-exhaustion panics mid-run.","Check for stop/attach flows that drop the log handle while stderr is still streaming."],"tags":["panic","thread","stderr","logging","lane","rust"],"backgroundTag":"thread-interrupted","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"}