{"record":{"id":"0045b640c01e540c","repo":"tinyhumansai/openhuman","slug":"shutdown-hooks-poisoned","errorCode":null,"errorMessage":"shutdown hooks poisoned","messagePattern":"shutdown hooks poisoned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/core/shutdown.rs","lineNumber":38,"sourceCode":"/// Register a cleanup function to run on graceful shutdown.\n///\n/// Use this to perform necessary cleanup tasks such as stopping background\n/// services, flushing caches, or closing database connections when the\n/// application is shutting down.\n///\n/// Hooks execute sequentially in the order they were registered.\n///\n/// # Arguments\n///\n/// * `hook` - A function that returns a future. The future will be awaited\n///   during the shutdown process.\npub fn register<F, Fut>(hook: F)\nwhere\n    F: Fn() -> Fut + Send + Sync + 'static,\n    Fut: Future<Output = ()> + Send + 'static,\n{\n    let boxed: ShutdownHook = Box::new(move || Box::pin(hook()));\n    HOOKS.lock().expect(\"shutdown hooks poisoned\").push(boxed);\n}\n\n/// Run all registered hooks (called once during shutdown).\n///\n/// This function drains the global `HOOKS` list and awaits each hook in sequence.\nasync fn run_hooks() {\n    let hooks: Vec<ShutdownHook> = {\n        let mut guard = HOOKS.lock().expect(\"shutdown hooks poisoned\");\n        // Use mem::take to clear the hooks list and take ownership of the vector.\n        std::mem::take(&mut *guard)\n    };\n    for hook in &hooks {\n        hook().await;\n    }\n}\n\n/// Returns a future that resolves when the process receives a termination\n/// signal (SIGINT on all platforms, plus SIGTERM on Unix), then runs all","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/core/shutdown.rs#L20-L56","documentation":"Mutex poisoning on the global shutdown-hook registry: a panic occurred inside another thread while it held the registry's lock (during register() or run_hooks()), so subsequent lock() calls return PoisonError and this expect fires. The root cause is the earlier panic visible in logs, not the shutdown path itself.","triggerScenarios":"Thrown at src/core/shutdown.rs:38 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Find and fix the panic that poisoned the lock (it precedes this error in the logs)","Use lock().unwrap_or_else(|e| e.into_inner()) to recover the registry if hooks are still needed","Register hooks early so registration cannot race a failing hook run"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}