{"record":{"id":"fe6f3bde30922b76","repo":"facebook/flow","slug":"handler-existed-during-typed-validation","errorCode":null,"errorMessage":"handler existed during typed validation","messagePattern":"handler existed during typed validation","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"rust_port/crates/flow_lsp_server/src/flow_lsp.rs","lineNumber":2408,"sourceCode":"        ) | (\n            lsp::LspResult::ConfigurationResult(_),\n            LspResultHandler::ConfigurationHandler(_),\n        ) | (\n            lsp::LspResult::RegisterCapabilityResult,\n            LspResultHandler::VoidHandler,\n        ) | (lsp::LspResult::ErrorResult(_, _), _)\n    );\n    if is_mistyped {\n        // | _ ->\n        return Err(internal_error_exception(format!(\n            \"Response {} has mistyped handler\",\n            lsp_fmt::result_name_to_string(&result)\n        )));\n    }\n    let handler = ienv\n        .i_outstanding_local_handlers\n        .remove(id)\n        .expect(\"handler existed during typed validation\");\n    ienv.i_outstanding_local_requests.remove(id);\n    let LspHandler {\n        on_response,\n        on_error,\n    } = handler;\n    let handler: Box<dyn FnOnce(&mut ServerState) -> Result<(), FlowLspError>> =\n        match (result, on_response) {\n            (\n                lsp::LspResult::ShowMessageRequestResult(result),\n                LspResultHandler::ShowMessageHandler(handle),\n            ) => Box::new(move |state| handle(result, state)),\n            (\n                lsp::LspResult::ShowStatusResult(result),\n                LspResultHandler::ShowStatusHandler(handle),\n            ) => Box::new(move |state| handle(result, state)),\n            (\n                lsp::LspResult::ApplyWorkspaceEditResult(result),\n                LspResultHandler::ApplyWorkspaceEditHandler(handle),","sourceCodeStart":2390,"sourceCodeEnd":2426,"githubUrl":"https://github.com/facebook/flow/blob/5c865861998a8ccb7dbc82b0c1f511e9ef60c3d9/rust_port/crates/flow_lsp_server/src/flow_lsp.rs#L2390-L2426","documentation":"An internal invariant of the LSP request/response machinery: when a response to a locally-issued request arrives, its id must still be present in i_outstanding_local_handlers so the typed (non-mistyped) response can be dispatched. The expect fires when the map no longer holds the id — the handler was already removed (duplicate response, earlier cancellation cleanup) or never existed (foreign/echoed response id).","triggerScenarios":"An LSP client sends the same response twice; a response arrives after the cancellation path already removed the handler; two server components reuse overlapping request ids; an editor bug echoes server-issued request ids back as responses.","commonSituations":"Flaky editor plugins answering showMessageRequest twice; races between cancellation (client) and slow responses (server); regressions in the port's id bookkeeping after refactoring the handler map.","solutions":["Make removal tolerant: replace expect with if-let-Some and treat a missing handler as a late/duplicate response (log and return)","Reproduce with LSP message tracing enabled and check whether the offending response id was sent twice","Audit cancellation/removal ordering so a handler is removed exactly once per request lifecycle"],"exampleFix":"// before\nlet handler = ienv.i_outstanding_local_handlers.remove(id)\n    .expect(\"handler existed during typed validation\");\n\n// after — tolerate a duplicate or late response\nlet Some(handler) = ienv.i_outstanding_local_handlers.remove(id) else {\n    log::warn!(\"duplicate/cancelled response for local request id {id:?}\");\n    ienv.i_outstanding_local_requests.remove(id);\n    return Ok(());\n};","handlingStrategy":"validation","validationCode":"// Validate the response id against outstanding requests before removal\nif !ienv.i_outstanding_local_handlers.contains_key(id) {\n    log::warn!(\"late/duplicate response for local request {id:?}\");\n    return Ok(());\n}\nlet handler = ienv.i_outstanding_local_handlers.remove(id).expect(\"checked above\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Make handler removal idempotent (if-let instead of expect) so duplicate responses can't crash the server","Remove a handler from exactly one place in the request lifecycle (response or cancellation, never both)","Enable LSP message tracing when this fires to identify the duplicate/foreign response id"],"tags":["rust","lsp","invariant","hashmap","race-condition"],"backgroundTag":"response-without-pending-request","analyzedSha":"5c865861998a8ccb7dbc82b0c1f511e9ef60c3d9","analyzedAt":"2026-08-20T10:41:37.992Z","contentChangedAt":"2026-08-20T10:41:37.992Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}