Skip to content

Fix forked process console forwarding reporting shared references as circular - #335594

Open
Leo Camus (Dev-next-gen) wants to merge 2 commits into
microsoft:mainfrom
Dev-next-gen:fix/bootstrap-fork-shared-refs
Open

Fix forked process console forwarding reporting shared references as circular#335594
Leo Camus (Dev-next-gen) wants to merge 2 commits into
microsoft:mainfrom
Dev-next-gen:fix/bootstrap-fork-shared-refs

Conversation

@Dev-next-gen

Copy link
Copy Markdown

While fixing safeStringify in #335589 I noticed that src/bootstrap-fork.ts has its own copy of the same replacer. safeToString, which forwards console.* calls from forked processes to the parent when VSCODE_VERBOSE_LOGGING is set (the pty host, the file watcher and the agent host starters all set it), pushes every object into a seen array and never removes anything. So an object that is logged twice, or referenced from two properties, reaches the parent as "[Circular]" after its first occurrence, even though there is no cycle.

With a forked fixture that runs

const shared = { value: 1 };
console.log(shared, { a: shared, b: shared }, circular); // circular.self = circular

the __$console message the parent receives is:

before: [{"value":1},{"a":"[Circular]","b":"[Circular]"},{"name":"circular","self":"[Circular]"}]
after:  [{"value":1},{"a":{"value":1},"b":{"value":1}},{"name":"circular","self":"[Circular]"}]

The fix is the same one as in #335589 and in stableStringify (#327398). The replacer only tracks the current ancestor path, and uses this (the holder) to pop subtrees that are finished. Real cycles are still replaced with "[Circular]". I removed the isObject helper because every object has to go on the stack for the holder check to work. Together with the Array.isArray check, the only objects it left out were RegExp and Date instances, and a Date has already been turned into a string by toJSON by the time the replacer sees it.

How I tested it: I added a case to processes.integrationTest.ts that forks bootstrap-fork with a new fork_console fixture and checks the forwarded arguments, including a genuine cycle. It fails against the current bootstrap-fork.ts with the "before" output above and passes with the fix, and the other tests in the file still pass. I ran it with test/unit/node/index.js --run src/vs/base/test/node/processes/processes.integrationTest.ts, with the Node version check commented out because the machine I used has Node 22. eslint is clean on the changed files and typecheck-client reports nothing in them.

There is no issue for this that I could find. This PR doesn't depend on #335589, they touch different files.

AI tools used

…circular

The JSON.stringify replacer in bootstrap-fork's safeToString pushed every
object into a `seen` array and never removed anything, so a value that is
referenced twice in the console arguments of a forked process (a DAG, not
a cycle) was forwarded to the parent as "[Circular]" after its first
occurrence. For example console.log(shared, { a: shared, b: shared })
arrived as [{"value":1},{"a":"[Circular]","b":"[Circular]"}].

Track only the current ancestor path instead, using the replacer's `this`
(the holder) to drop subtrees that are finished. True cycles are still
reported as "[Circular]". The isObject helper is gone: every object has
to be tracked for the holder check to work, and RegExp/Date were the only
exclusions (a Date is already a string by the time the replacer sees it).

Added an integration test that forks bootstrap-fork with a fixture that
logs a shared reference and a genuine cycle.
Copilot AI balanced review requested due to automatic review settings September 10, 2026 23:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The implementation and focused integration test correctly cover the regression; the remaining comment-style feedback is non-blocking.

Pull request overview

Fixes console forwarding so shared object references are serialized normally while genuine cycles remain protected.

Changes:

  • Tracks only the active ancestor path during serialization.
  • Adds integration coverage for shared references and cycles.
  • Adds a forked-process fixture producing representative console output.
File summaries
File Description
src/bootstrap-fork.ts Corrects circular-reference detection.
src/vs/base/test/node/processes/processes.integrationTest.ts Verifies forwarded console arguments.
src/vs/base/test/node/processes/fixtures/fork_console.ts Provides shared and circular test values.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/bootstrap-fork.ts Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants