Skip to content

fix: enrich truncated IPC object deserialize error (fixes #330263) #330276

Description

@vs-code-engineering

Summary

Telemetry bucket 112c22ba-c8f9-9d2c-1f93-9f46466fb2ac reports SyntaxError: Unexpected end of JSON input thrown from JSON.parse inside deserialize() in src/vs/base/parts/ipc/common/ipc.ts, running in the agent host utility process (agentHostMain.js) while decoding an inbound IPC message delivered over a MessagePort. The message payload arrives truncated: the binary framing declares an object of N bytes, but fewer than N bytes are present, so JSON.parse on a partial/empty string fails with a context-free error that gives triage no way to see where the corruption happened. New in 1.133.0-insider (5a36a9c6); not present in the 1.132.0 baseline (df53daab), matching the introduction of the Agent Host Protocol-over-IPC (MessagePort) plumbing.

Fixes #330263
Recommended reviewer: @connor4312

Culprit Commit

Ancestry could not be git-verified in this environment (blobless checkout, credentials stripped after checkout, so git show <hash> / merge-base --is-ancestor fail on on-demand object fetch). Within the 1.132.0 to 1.133.0-insider regression window, the agent-host MessagePort transport that routes these IPC messages was introduced/reworked by:

  • 63a042819ddd4deaffd45ac84dc176d1f00bb49a — "agentHost: use AHP for local connections" (routes local renderer traffic through the Agent Host Protocol over MessagePort) — Connor Peet (@connor4312).

This is the most likely producer of the new truncated-frame path; the crash surfaces in the shared deserialize() utility rather than in the transport commit itself.

Code Flow

flowchart TD
    A[MessagePortMain message event - native Electron layer] -->|e.data possibly truncated| B[Protocol.onMessage ipc.mp.ts:21-26 VSBuffer.wrap or alloc 0]
    B --> C[ChannelServer.onRawMessage / onBuffer ipc.ts]
    C --> D[deserialize reader ipc.ts:304]
    D -->|DataType.Object| E[readIntVQL yields length N]
    E --> F[reader.read N - BufferReader.slice returns fewer than N bytes, NO error on underflow]
    F --> G[JSON.parse of partial string ipc.ts:322]
    G -->|SyntaxError: Unexpected end of JSON input| H[telemetry bucket 112c22ba]
Loading

Affected Files

  • src/vs/base/parts/ipc/common/ipc.tsdeserialize() DataType.Object case (crash site and where the truncation first becomes observable). BufferReader.read() (lines 217-221) slices without erroring on underflow, so a short buffer silently yields a partial string.

Repro Steps

Deterministic user repro is not available (the truncation originates in the native MessagePort/utility-process boundary during agent host connect/disconnect, not in reproducible TS logic). Observed across Windows/Mac/Linux, 44 users, on 1.133.0-insider. Conceptually: deliver a MessagePort frame to the agent host whose binary object header declares more bytes than the payload actually contains (e.g. a partial/racing frame during agent host teardown) and deserialize will attempt JSON.parse on a truncated string.

How the Fix Works

Chosen approachsrc/vs/base/parts/ipc/common/ipc.ts, deserialize() DataType.Object case (the bypass site is ipc.ts:322, where reader.read(N) can return fewer than N bytes without error because BufferReader.read slices without bounds-checking). Before calling JSON.parse, read the declared length N and the actual buffer, and if the buffer is shorter than declared, throw an enriched error naming the expected vs. received byte counts. This follows the cross-process-error principle: enrich the error with diagnostic context, do not swallow it. The error is still thrown and still reaches the telemetry pipeline, but the new bucket will carry actionable framing information (expected N, received M) instead of an opaque Unexpected end of JSON input, letting the transport owner see truncation is occurring and where. This does not add a try/catch, does not remove any logService.error, and does not coerce the bad value to a benign default — the corrupt frame is still rejected loudly.

Alternatives considered

  • Wrap JSON.parse in try/catch and return undefined/empty object — rejected: swallows a real cross-process corruption, hides the producer, and breaks the telemetry signal that surfaced the bug.
  • Drop empty/short messages at the producer in Protocol.onMessage (ipc.mp.ts) — rejected as the primary fix: the true producer is the native MessagePort layer (not visible in TS), the exact truncation condition isn't reconstructable from source, and silently dropping frames there would mask the corruption for every channel without diagnostics. Enriching at the deserialization boundary keeps the signal while making it diagnosable for whichever transport is at fault.

Recommended Owner

@connor4312 — authored the 1.133 Agent Host Protocol-over-MessagePort IPC plumbing (63a042819dd, "agentHost: use AHP for local connections") that introduced the new transport path in which this error appears.

Generated by errors-fix · opus48 · 662.4 AIC · ⌖ 11.4 AIC · ⊞ 18.6K ·


Note

This was originally intended as a pull request, but PR creation failed. The changes have been pushed to the branch fix/ipc-truncated-object-deserialize-330263-c312214413ed2ef7.

Original error: ERR_API: [2026-08-11T15:29:09.168Z] create pull request in microsoft/vscode failed (attempt 1)

Original error: Validation Failed: {"resource":"PullRequest","code":"custom","field":"fork_collab","message":"fork_collab Fork collab can't be granted by someone without permission"} - https://docs.github.com/rest/pulls/pulls#create-a-pull-request
Retryable: false
Suggestion: This error cannot be resolved by retrying. Please check the error details and fix the underlying issue.

To create the pull request manually:

gh pr create --title "fix: enrich truncated IPC object deserialize error (fixes #330263)" --base main --head vscodebot-pr:fix/ipc-truncated-object-deserialize-330263-c312214413ed2ef7 --repo microsoft/vscode
Show patch (39 lines)
From b29b519045105f609793083a867227410584e5d7 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Tue, 11 Aug 2026 15:20:18 +0000
Subject: [PATCH] fix: enrich truncated IPC object deserialize error (fixes
 #330263)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
 src/vs/base/parts/ipc/common/ipc.ts | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts
index dcbe97b09df..ab632f9ae55 100644
--- a/src/vs/base/parts/ipc/common/ipc.ts
+++ b/src/vs/base/parts/ipc/common/ipc.ts
@@ -319,7 +319,18 @@ export function deserialize(reader: IReader): any {
 
 			return result;
 		}
-		case DataType.Object: return JSON.parse(reader.read(readIntVQL(reader)).toString());
+		case DataType.Object: {
+			const length = readIntVQL(reader);
+			const buffer = reader.read(length);
+			if (buffer.byteLength < length) {
+				// The underlying message was truncated (e.g. a partial frame crossing a
+				// process boundary such as a MessagePort). Surface a diagnosable error with
+				// framing context instead of an opaque `JSON.parse` "Unexpected end of JSON
+				// input" that hides where the corruption happened.
+				throw new Error(`Truncated IPC object payload: expected ${length} bytes, received ${buffer.byteLength}`);
+			}
+			return JSON.parse(buffer.toString());
+		}
 		case DataType.Int: return readIntVQL(reader);
 	}
 }
-- 
2.54.0

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions