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

  1. Restart the language server or re-run linting so fresh diagnostics with the current schema are published and cached
  2. Ensure the client passes diagnostics back byte-for-byte, especially the opaque `data` field
  3. Keep the extension and server versions in sync; clear cached diagnostics after upgrades
  4. 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

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.