astral-sh/ruff · error
InternalError
InternalError
Error message
failed to deserialize diagnostic data: {err} What it means
When resolving fixes, the server takes diagnostics it previously published (those with source 'ruff' and a `data` payload) and deserializes `data` into an internal AssociatedDiagnosticData structure containing the fix edits, title, and noqa edit. If the diagnostic sent back by the client carries a `data` field that is not that JSON shape, serde fails with this InternalError.
Source
Thrown at crates/ruff_server/src/lint.rs:305
document: CheckedDocument::Toml(document),
}
}
/// Converts LSP diagnostics to a list of `DiagnosticFix`es by deserializing associated data on each diagnostic.
pub(crate) fn fixes_for_diagnostics(
diagnostics: Vec<lsp_types::Diagnostic>,
) -> crate::Result<Vec<DiagnosticFix>> {
diagnostics
.into_iter()
.filter(|diagnostic| diagnostic.source.as_deref() == Some(DIAGNOSTIC_NAME))
.map(move |mut diagnostic| {
let Some(data) = diagnostic.data.take() else {
return Ok(None);
};
let fixed_diagnostic = diagnostic;
let associated_data: crate::lint::AssociatedDiagnosticData =
serde_json::from_value(data).map_err(|err| {
anyhow::anyhow!("failed to deserialize diagnostic data: {err}")
})?;
Ok(Some(DiagnosticFix {
fixed_diagnostic,
code: associated_data.code,
title: associated_data.title,
noqa_edit: associated_data.noqa_edit,
edits: associated_data.edits,
is_preferred: associated_data.is_preferred,
}))
})
.filter_map(crate::Result::transpose)
.collect()
}
enum CheckedDocument<'a> {
Python {
source: SourceKind,
index: LineIndex,View on GitHub (pinned to d1087a4b9e)
Solutions
- Restart the language server or re-run linting so fresh diagnostics with the current schema are published and cached
- Ensure the client passes diagnostics back byte-for-byte, especially the opaque `data` field
- Keep the extension and server versions in sync; clear cached diagnostics after upgrades
- Filter to diagnostics with source === 'ruff' before invoking fix resolution
Example fix
// before: resolving a diagnostic whose data was rewritten by middleware
await conn.sendRequest('codeAction/resolve', { ...action }); // data mangled -> deserialization error
// after: keep ruff diagnostics untouched
const ruffDiagnostics = allDiagnostics.filter(d => d.source === 'ruff' && d.data !== undefined);
// pass these, unmodified, into fix resolution Defensive patterns
Strategy: validation
Validate before calling
// Only feed ruff-origin diagnostics with intact data payloads into fix resolution
const resolvable = diagnostics.filter(
(d) => d.source === 'ruff' && d.data !== null && d.data !== undefined,
);
if (!resolvable.some(d => d === action.diagnostics?.[0])) {
throw new Error('action carries a non-ruff or degraded diagnostic');
} Type guard
function hasRuffData(d: lsp.Diagnostic): boolean {
return d.source === 'ruff' && d.data !== undefined && typeof d.data === 'object';
} Prevention
- Treat diagnostic.data as opaque and pass it back byte-identical
- Clear cached ruff diagnostics whenever the server version changes or restarts
- Avoid letting other extensions rewrite diagnostics that will be echoed to the server
When it happens
Trigger: Another extension or the client mutating/stripping the diagnostic `data` before it is echoed back; sending diagnostics produced by a different tool that happen to use source 'ruff'; stale diagnostics published by an older server version still cached client-side after an upgrade (schema drift between versions).
Common situations: Editor extensions that rewrite diagnostics for display and lose the opaque data payload; upgrading the Ruff server without clearing diagnostic caches; multiple linters interleaving diagnostics in one editor.
Related errors
AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20).
Data as JSON: /api/errors/97981d78a61b1f9a.
Report an issue: GitHub.