Skip to content

Sanitize non-finite range coordinates in Diagnostic.from#318520

Open
yogeshwaran-c wants to merge 1 commit into
microsoft:mainfrom
yogeshwaran-c:fix/validate-diagnostic-range
Open

Sanitize non-finite range coordinates in Diagnostic.from#318520
yogeshwaran-c wants to merge 1 commit into
microsoft:mainfrom
yogeshwaran-c:fix/validate-diagnostic-range

Conversation

@yogeshwaran-c
Copy link
Copy Markdown
Contributor

Fixes #177094

Problem

Diagnostics with non-finite range coordinates (NaN, Infinity) are passed through Diagnostic.from into IMarkerData unchanged. When marshalled across the RPC boundary to the main thread (and onward to LSP language servers), NaN is serialized as JSON null, which violates the LSP spec (uinteger required) and causes downstream language servers to fail when handling code action requests.

The original report shows the malformed payload:

"start": { "line": null, "character": 0 }

This is the case @jrieken called out in #177094 (comment): "diagnostics are created for ill-formed ranges (nan, null, -1) and our validation doesn't catch all of them ... anyone is invited to improve range validation."

Fix

After converting value.range via Range.from(...), check that all four coordinates are finite. If any aren't, replace the range with the known-good degenerate range (1,1)-(1,1). The diagnostic is preserved (extension authors still see their message in the Problems view) and downstream consumers + LSP clients receive a valid range.

Scope is intentionally narrow: only the diagnostic conversion path. Range.from itself is unchanged, and other call sites (location, document symbol, etc.) are unaffected.

Tests

Added a Diagnostic suite to extHostTypeConverters.test.ts covering:

  • Well-formed range passes through unchanged
  • All-NaN range is sanitized to (1,1,1,1)
  • Mixed-validity range (one NaN, others valid) is sanitized
  • Infinity is sanitized

Diagnostics whose range contains non-finite numbers (NaN, Infinity) currently
flow through to IMarkerData unchanged. When marshalled across the RPC
boundary, NaN is serialized as JSON null, which breaks LSP code action
requests (the spec requires uinteger line/character values).

Sanitize the converted range in Diagnostic.from: if any of the four
coordinates is non-finite, replace the range with a known-good (1,1,1,1)
default so downstream IMarkerData consumers and LSP clients receive a
valid range.

Fixes microsoft#177094
Copilot AI review requested due to automatic review settings May 27, 2026 06:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds sanitization for Diagnostic range coordinates to prevent non-finite values (NaN/Infinity) from producing invalid JSON/LSP requests, and introduces regression tests to validate the converter behavior.

Changes:

  • Sanitize converted diagnostic ranges when any coordinate is non-finite.
  • Add unit tests covering well-formed ranges and NaN/Infinity edge cases for diagnostics.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/vs/workbench/api/common/extHostTypeConverters.ts Sanitizes diagnostic range coordinates to avoid non-finite values propagating into marker DTOs.
src/vs/workbench/api/test/common/extHostTypeConverters.test.ts Adds tests to ensure Diagnostic.from preserves valid ranges and sanitizes NaN/Infinity inputs.

Comment on lines +256 to +273
// Sanitize range to ensure all coordinates are finite numbers. Non-finite
// values (NaN, Infinity) get serialized to JSON null and produce invalid
// LSP code action requests, see https://github.com/microsoft/vscode/issues/177094.
const range = Range.from(value.range);
if (range && (
!Number.isFinite(range.startLineNumber)
|| !Number.isFinite(range.startColumn)
|| !Number.isFinite(range.endLineNumber)
|| !Number.isFinite(range.endColumn)
)) {
range.startLineNumber = 1;
range.startColumn = 1;
range.endLineNumber = 1;
range.endColumn = 1;
}

return {
...Range.from(value.range),
...range,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree here. We should however make sure to not create a range with a end -> start direction. So if end is non-finite it should be > start for both line and character.

Comment on lines +290 to +300
test('from sanitizes Infinity positions to a valid range', function () {
const diag = new Diagnostic(
new Range(new Position(0, 0), new Position(Infinity, 0)),
'broken',
);
const marker = DiagnosticConverter.from(diag);
assert.ok(Number.isFinite(marker.startLineNumber));
assert.ok(Number.isFinite(marker.startColumn));
assert.ok(Number.isFinite(marker.endLineNumber));
assert.ok(Number.isFinite(marker.endColumn));
});
Comment on lines +256 to +273
// Sanitize range to ensure all coordinates are finite numbers. Non-finite
// values (NaN, Infinity) get serialized to JSON null and produce invalid
// LSP code action requests, see https://github.com/microsoft/vscode/issues/177094.
const range = Range.from(value.range);
if (range && (
!Number.isFinite(range.startLineNumber)
|| !Number.isFinite(range.startColumn)
|| !Number.isFinite(range.endLineNumber)
|| !Number.isFinite(range.endColumn)
)) {
range.startLineNumber = 1;
range.startColumn = 1;
range.endLineNumber = 1;
range.endColumn = 1;
}

return {
...Range.from(value.range),
...range,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree here. We should however make sure to not create a range with a end -> start direction. So if end is non-finite it should be > start for both line and character.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LSP Code Action Requests with wrong types are sent

3 participants