facebook/flow · critical
handler existed during typed validation
Error message
handler existed during typed validation
What it means
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).
Source
Thrown at rust_port/crates/flow_lsp_server/src/flow_lsp.rs:2408
) | (
lsp::LspResult::ConfigurationResult(_),
LspResultHandler::ConfigurationHandler(_),
) | (
lsp::LspResult::RegisterCapabilityResult,
LspResultHandler::VoidHandler,
) | (lsp::LspResult::ErrorResult(_, _), _)
);
if is_mistyped {
// | _ ->
return Err(internal_error_exception(format!(
"Response {} has mistyped handler",
lsp_fmt::result_name_to_string(&result)
)));
}
let handler = ienv
.i_outstanding_local_handlers
.remove(id)
.expect("handler existed during typed validation");
ienv.i_outstanding_local_requests.remove(id);
let LspHandler {
on_response,
on_error,
} = handler;
let handler: Box<dyn FnOnce(&mut ServerState) -> Result<(), FlowLspError>> =
match (result, on_response) {
(
lsp::LspResult::ShowMessageRequestResult(result),
LspResultHandler::ShowMessageHandler(handle),
) => Box::new(move |state| handle(result, state)),
(
lsp::LspResult::ShowStatusResult(result),
LspResultHandler::ShowStatusHandler(handle),
) => Box::new(move |state| handle(result, state)),
(
lsp::LspResult::ApplyWorkspaceEditResult(result),
LspResultHandler::ApplyWorkspaceEditHandler(handle),View on GitHub (pinned to 5c86586199)
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
Example fix
// before
let handler = ienv.i_outstanding_local_handlers.remove(id)
.expect("handler existed during typed validation");
// after — tolerate a duplicate or late response
let Some(handler) = ienv.i_outstanding_local_handlers.remove(id) else {
log::warn!("duplicate/cancelled response for local request id {id:?}");
ienv.i_outstanding_local_requests.remove(id);
return Ok(());
}; Defensive patterns
Strategy: validation
Validate before calling
// Validate the response id against outstanding requests before removal
if !ienv.i_outstanding_local_handlers.contains_key(id) {
log::warn!("late/duplicate response for local request {id:?}");
return Ok(());
}
let handler = ienv.i_outstanding_local_handlers.remove(id).expect("checked above"); Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid position: {{line: {}; column: {}}}
- convert_to_server_uris: invalid file url
- lock file is already held
- invalid line
- invalid column
AI-assisted analysis of facebook/flow@5c86586199 (2026-08-20).
Data as JSON: /api/errors/fe6f3bde30922b76.
Report an issue: GitHub.