{"record":{"id":"e726a9206bdfb8ca","repo":"FuelLabs/sway","slug":"failed-to-send-terminate-message","errorCode":null,"errorMessage":"failed to send terminate message","messagePattern":"failed to send terminate message","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sway-lsp/src/server_state.rs","lineNumber":336,"sourceCode":"            // We are still compiling, lets wait to be notified.\n            self.finished_compilation.notified().await;\n        }\n    }\n\n    pub fn shutdown_server(&self) -> jsonrpc::Result<()> {\n        let _p = tracing::trace_span!(\"shutdown_server\").entered();\n        tracing::info!(\"Shutting Down the Sway Language Server\");\n\n        // Drain pending compilation requests\n        while self.cb_rx.try_recv().is_ok() {}\n\n        // Set the retrigger_compilation flag to true so that the compilation exits early\n        self.retrigger_compilation.store(true, Ordering::SeqCst);\n\n        // Send a terminate message to the compilation thread\n        self.cb_tx\n            .send(TaskMessage::Terminate)\n            .expect(\"failed to send terminate message\");\n\n        // Delete all temporary directories.\n        for entry in self.sync_workspaces.iter() {\n            entry.value().remove_temp_dir();\n        }\n\n        Ok(())\n    }\n\n    pub(crate) async fn publish_diagnostics(\n        &self,\n        uri: Url,\n        workspace_uri: Url,\n        session: Arc<Session>,\n    ) {\n        let diagnostics = self.diagnostics(&uri, session.clone());\n        // Note: Even if the computed diagnostics vec is empty, we still have to push the empty Vec\n        // in order to clear former diagnostics. Newly pushed diagnostics always replace previously pushed diagnostics.","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/FuelLabs/sway/blob/47e5e902faa42baf652dd6a0c88cd23390c1a614/sway-lsp/src/server_state.rs#L318-L354","documentation":"During LSP shutdown, shutdown_server drains pending compilations, sets the retrigger flag, then sends TaskMessage::Terminate to the compilation thread over cb_tx. mpsc::send fails only when the receiver has been dropped — the compilation thread already exited (crashed earlier or the channel closed). The expect converts that into a panic, crashing the server in the middle of its own shutdown sequence.","triggerScenarios":"The compilation thread panicked during an earlier build leaving cb_tx dangling; a second shutdown/exit request after the first already terminated the thread; a race where the compile thread exits between the drain loop and the send.","commonSituations":"Editor restart flows issuing repeated shutdowns; test harnesses that call shutdown twice; the server left in a degraded state after an internal compile-thread panic, then asked to shut down.","solutions":["As an end user: avoid duplicate shutdown requests — issue one `shutdown` then `exit`, and restart the LSP process if it is wedged.","Upgrade sway-lsp; the send should tolerate a dead receiver: `if self.cb_tx.send(TaskMessage::Terminate).is_err() { tracing::warn!(\"compile thread already terminated\"); }`.","If you embed sway-lsp, wrap the shutdown path in std::panic::catch_unwind so a dead compile thread cannot abort your host."],"exampleFix":"// before (sway-lsp/src/server_state.rs:336)\nself.cb_tx.send(TaskMessage::Terminate).expect(\"failed to send terminate message\");\n// after\nif self.cb_tx.send(TaskMessage::Terminate).is_err() {\n    tracing::warn!(\"compilation thread already terminated; skipping Terminate\");\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// embedders: tolerate a wedged server during teardown\nlet result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    server.shutdown_server()\n}));\nif result.is_err() {\n    tracing::warn!(\"server panicked during shutdown; treating as terminated\");\n}","preventionTips":["Send exactly one LSP `shutdown` then `exit`; never repeat shutdown on the same session.","Restart (kill) a wedged sway-lsp process instead of issuing redundant requests.","Monitor for compile-thread panics in logs — they are what leaves cb_tx dangling for the next shutdown."],"tags":["rust","sway-lsp","shutdown","channel","panic","race-condition","lifecycle"],"backgroundTag":null,"analyzedSha":"47e5e902faa42baf652dd6a0c88cd23390c1a614","analyzedAt":"2026-08-16T07:57:45.555Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}