{"record":{"id":"4bb3d0a1533d54d8","repo":"zellij-org/zellij","slug":"program-terminates","errorCode":null,"errorMessage":"Program terminates","messagePattern":"Program terminates","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"zellij-utils/src/errors.rs","lineNumber":160,"sourceCode":"}\n\n/// Helper function to silence `#[warn(unused_must_use)]` cargo warnings. Used exclusively in\n/// `FatalError::non_fatal`!\nfn discard_result<T>(_arg: anyhow::Result<T>) {}\n\nimpl<T> FatalError<T> for anyhow::Result<T> {\n    fn non_fatal(self) {\n        if self.is_err() {\n            discard_result(self.context(\"a non-fatal error occured\").to_log());\n        }\n    }\n\n    fn fatal(self) -> T {\n        if let Ok(val) = self {\n            val\n        } else {\n            self.context(\"a fatal error occured\")\n                .expect(\"Program terminates\")\n        }\n    }\n}\n\n/// Different types of calls that form an [`ErrorContext`] call stack.\n///\n/// Complex variants store a variant of a related enum, whose variants can be built from\n/// the corresponding Zellij MSPC instruction enum variants ([`ScreenInstruction`],\n/// [`PtyInstruction`], [`ClientInstruction`], etc).\n#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]\npub enum ContextType {\n    /// A screen-related call.\n    Screen(ScreenContext),\n    /// A PTY-related call.\n    Pty(PtyContext),\n    /// A plugin-related call.\n    Plugin(PluginContext),\n    /// An app-related call.","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/zellij-org/zellij/blob/98a0837077492d53dd252ab30bc3e43e41e504f4/zellij-utils/src/errors.rs#L142-L178","documentation":"Not an independent fault: FatalError::fatal() is zellij's terminal error handler, and 'Program terminates' is the expect fired when an anyhow::Result reaching .fatal() is Err. The method re-wraps the error with the context 'a fatal error occured' and unwraps it, intentionally aborting the process. Whatever io/spawn/serde failure flowed into that call site is the real cause; the ErrorContext call stack (Pty/Screen/Plugin/Client) logged just before the panic identifies it.","triggerScenarios":"Any `.fatal()` call site whose Result is Err - e.g. child.wait() or handle_command_exit failures on the pty path, io errors during teardown - after the anyhow context chain has been attached.","commonSituations":"Any zellij crash whose final line is 'Program terminates': the actionable cause is the ErrorContext/anyhow chain printed above it, not this message.","solutions":["Read upwards from the panic: the ErrorContext stack and 'a fatal error occured' chain name the failing call and its cause","Fix the underlying error at the call site it identifies (missing command, io failure, serialization mismatch)","If the failure is genuinely unrecoverable, prefer exiting with the error printed rather than the generic panic text","Search the exact ErrorContext text in zellij's issue tracker; these chains map to known failure modes"],"exampleFix":"// before\nchild.wait().with_context(|| err_context(&cmd)).fatal();\n\n// after - branch on the error instead of unconditional termination\nmatch child.wait().with_context(|| err_context(&cmd)) {\n    Ok(status) => { /* ... */ }\n    Err(err) => {\n        log::error!(\"wait failed: {err:#}\");\n        quit_cb(PaneId::Terminal(terminal_id), None, cmd); // recover per-pane\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// before calling .fatal(), decide whether the error is truly terminal\nmatch risky_operation().with_context(|| err_context(&cmd)) {\n    Ok(val) => val,\n    Err(err) if is_recoverable(&err) => {\n        log::warn!(\"recoverable: {err:#}\");\n        default_or_retry()\n    }\n    Err(err) => {\n        log::error!(\"fatal: {err:#}\");\n        std::process::exit(1); // explicit, message-carrying exit\n    }\n}","preventionTips":["Use .non_fatal() for errors the session can survive","Log the full anyhow chain ({err:#}) before terminating","Only call .fatal() at true process boundaries; per-pane/per-thread failures should degrade, not abort","Correlate every fatal exit with its ErrorContext to build a failure catalog"],"tags":["rust","zellij","fatal-error","anyhow","error-context","panic"],"backgroundTag":null,"analyzedSha":"98a0837077492d53dd252ab30bc3e43e41e504f4","analyzedAt":"2026-08-16T13:02:01.396Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}