{"record":{"id":"2317c474bdf01965","repo":"rust-lang/rust-analyzer","slug":"invalid-lsp-resolve-data","errorCode":null,"errorMessage":"Invalid LSP resolve data","messagePattern":"Invalid LSP resolve data","errorType":"error_code","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/rust-analyzer/src/handlers/request.rs","lineNumber":1859,"sourceCode":"pub(crate) fn handle_inlay_hints_resolve(\n    snap: GlobalStateSnapshot,\n    mut original_hint: InlayHint,\n) -> anyhow::Result<InlayHint> {\n    let _p = tracing::info_span!(\"handle_inlay_hints_resolve\").entered();\n\n    let Some(data) = original_hint.data.take() else {\n        return Ok(original_hint);\n    };\n    let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?;\n    let file_id = FileId::from_raw(resolve_data.file_id);\n    if resolve_data.version != snap.file_version(file_id) {\n        tracing::warn!(\"Inlay hint resolve data is outdated\");\n        return Ok(original_hint);\n    }\n    let Some(hash) = resolve_data.hash.parse().ok() else {\n        return Ok(original_hint);\n    };\n    anyhow::ensure!(snap.file_exists(file_id), \"Invalid LSP resolve data\");\n\n    let line_index = snap.file_line_index(file_id)?;\n    let range = from_proto::text_range(&line_index, resolve_data.resolve_range)?;\n\n    let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(snap.minicore());\n    forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty();\n    let resolve_hints = snap.analysis.inlay_hints_resolve(\n        &forced_resolve_inlay_hints_config,\n        file_id,\n        range,\n        hash,\n        |hint| {\n            std::hash::BuildHasher::hash_one(\n                &std::hash::BuildHasherDefault::<ide_db::FxHasher>::default(),\n                hint,\n            )\n        },\n    )?;","sourceCodeStart":1841,"sourceCodeEnd":1877,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/crates/rust-analyzer/src/handlers/request.rs#L1841-L1877","documentation":"`handle_inlay_hints_resolve` stores an opaque resolve blob (range, hash, etc.) inside inlay hint `data`. Before re-resolving, it validates that the referenced file still exists in the snapshot via `snap.file_exists(file_id)`; if not, the stored data is considered corrupt or stale in a way that cannot be gracefully recovered, so it fails with `Invalid LSP resolve data` via `anyhow::ensure!`.","triggerScenarios":"A client sends `inlayHint/resolve` with hint `data` whose `file_id`/`hash` blob points to a file that no longer exists in the server's vfs snapshot (file deleted, workspace changed, or a fabricated/tampered `data` payload).","commonSituations":"Stale editor state resolving hints after a file was closed/deleted; protocol fuzzing or third-party clients reusing cached hint data across sessions; version drift between a saved hint and the current workspace.","solutions":["Have the client discard the stale hint and re-request `textDocument/inlayHint` for the current document.","Reopen/reload the file in the editor so fresh hints (without stale data) are produced.","If implementing a client, never cache inlay hint `data` beyond the server session.","As a workaround, restart the language server to reset vfs state."],"exampleFix":"// client-side before\nresolveHint(oldCachedHint);\n\n// after\nconst hints = await requestInlayHints(uri);\nawait resolveHint(hints.find(h => h.position === pos));","handlingStrategy":"try-catch","validationCode":"// client-side: only resolve hints obtained in the same session for a still-open document\nfunction canResolve(hint: InlayHint, openUris: Set<string>): boolean {\n  const fid = (hint.data as any)?.file_id;\n  return hint.data != null && fid != null && openUris.has(currentUri);\n}","typeGuard":"function hasValidResolveData(hint: InlayHint): boolean {\n  const d = hint.data as any;\n  return !!d && typeof d === \"object\" && \"resolve_range\" in d && \"hash\" in d && \"file_id\" in d;\n}","tryCatchPattern":"try {\n  return await client.sendRequest(\"inlayHint/resolve\", hint);\n} catch (e) {\n  // stale/invalid resolve data: drop the blob and refetch hints for the document\n  log.warn(\"inlayHint/resolve failed, refetching\", e);\n  return (await client.sendRequest(\"textDocument/inlayHint\", { textDocument: { uri }, range: fullRange }))[0] ?? hint;\n}","preventionTips":["Never reuse inlay hint `data` blobs across LSP sessions or file deletions.","Re-request inlay hints whenever the document is saved/reloaded externally.","Treat resolve failures as non-fatal and fall back to the un-resolved hint."],"tags":["lsp","inlay-hints","stale-data","rust-analyzer"],"backgroundTag":"stale-resolve-data","analyzedSha":"e8f7e90aa3e7b26aa9a000200f606c1078da99ec","analyzedAt":"2026-09-03T21:08:06.959Z","contentChangedAt":"2026-09-03T21:08:06.959Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}