Skip to content

fix: agent hang from leaked external-edit lock#326067

Draft
stevenh wants to merge 5 commits into
microsoft:mainfrom
stevenh:agents/fix-permission-issues-analysis
Draft

fix: agent hang from leaked external-edit lock#326067
stevenh wants to merge 5 commits into
microsoft:mainfrom
stevenh:agents/fix-permission-issues-analysis

Conversation

@stevenh

@stevenh stevenh commented Jul 15, 2026

Copy link
Copy Markdown

A single agent tool call that writes multiple files tracked all of them under one edit key, so later files overwrote earlier ones. The overwritten edit never acknowledged, VS Code core leaked that file's streaming-edit lock, and every later write to the same file hung the agent turn with no user action.

ExternalEditTracker now tracks each file separately per edit key and completes them all, so no lock is leaked. Waiting for a core acknowledgment is also bounded by cancellation and a safety timeout so a dropped ack can never wedge a turn, and bad-path diagnostics were added to make any future stall visible. Shared by the Copilot CLI and Claude agents.

Fixes #320292

Copilot AI review requested due to automatic review settings July 15, 2026 22:31
@stevenh stevenh marked this pull request as draft July 15, 2026 22:31
@stevenh

stevenh commented Jul 15, 2026

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="Rocket Science"

1 similar comment
@stevenh

stevenh commented Jul 15, 2026

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="Rocket Science"

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.

Pull request overview

Bounds external-edit acknowledgments to prevent write-permission hangs.

Changes:

  • Adds cancellation and timeout handling.
  • Bounds edit finalization waits.
  • Adds regression tests for stalled acknowledgments.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
permissionHelpers.ts Forwards cancellation tokens.
externalEditTracker.ts Adds bounded acknowledgment handling.
externalEditTracker.spec.ts Tests timeout and cancellation paths.

Comment thread extensions/copilot/src/extension/chatSessions/common/externalEditTracker.ts Outdated

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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@stevenh

stevenh commented Jul 16, 2026

Copy link
Copy Markdown
Author

While this currently prevents the infinite hang, it doesn't address the underlying cause, using it as a base to identify that.

@stevenh stevenh changed the title fix: write-permission hangs fix: agent write-permission hang from leaked external-edit lock Jul 16, 2026
@stevenh stevenh changed the title fix: agent write-permission hang from leaked external-edit lock fix: agent hang from leaked external-edit lock Jul 16, 2026
@stevenh stevenh requested a review from Copilot July 16, 2026 14:19

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.

Pull request overview

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

Comment thread src/vs/workbench/api/browser/mainThreadChatAgents2.ts
stevenh and others added 5 commits July 16, 2026 15:32
Granting a write permission was gated on an unbounded, un-cancellable wait
for VS Code core to invoke the externalEdit proceed callback. When core
dropped that acknowledgment, the write handler awaited forever,
respondToPermission was never called, and the agent turn wedged mid-prompt
with no user action (microsoft#320292).

ExternalEditTracker.trackEdit now releases the permission gate on whichever
happens first: core acknowledges, the request is cancelled, or a safety
timeout elapses. completeEdit is likewise bounded so request finalization
cannot block on a stalled acknowledgment. The write-permission handler now
forwards its cancellation token into trackEdit so Stop unblocks a pending
write. Both the Copilot CLI and Claude agents share this tracker.

Fixes microsoft#320292
…ycle

The cancellation listener was disposed together with the timeout the moment the
permission gate settled (core acknowledged or the safety timeout elapsed). A
request cancelled after that point but before completeEdit was then a no-op:
the ongoing-edit map entry was never deleted and the deferred the externalEdit
proceed callback awaits was never completed, so a cancelled tool that emits no
completion event left both the callback and the map entry pending indefinitely.

Only the timeout is now tied to the permission gate and cleared on settle. The
cancellation listener lives for the whole edit lifecycle and is disposed either
when it fires or by completeEdit, so cancellation always releases the edit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…trackEdit

Add handler-level tests covering the auto-approval and confirmed
write-permission paths, asserting that handleWritePermission forwards the
request cancellation token (and tool call id / stream) into
editTracker.trackEdit. The existing handler tests passed no tool call or
stream and never asserted forwarded arguments, so dropping the token from
a call site would have left the suite green. These tests fail if the token
is not forwarded, guarding the fix for the write-permission hang.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Write permissions for agent tools are gated on VS Code core acknowledging
the externalEdit; when that ack never arrives the turn stalls until the
safety timeout fires (see microsoft#320292). Add bad-path diagnostics
along the ack path so a future occurrence reveals the cause instead of only
surfacing as a silent timeout:

- ExternalEditTracker: log an error (with edit key, file count, waited ms)
  when the start ack times out, and a warning when completion is never
  confirmed. Inject an optional ILogger from the Copilot CLI and Claude
  sessions.
- ChatEditingSession.startExternalEdits: add a watchdog that warns which
  phase (readiness gate, streaming-edit lock, or snapshotting) is stuck if
  the method does not resolve before the extension-side timeout.
- mainThreadChatAgents2: warn when an externalEdits chunk is dropped
  (missing editing session/response), distinguishing a missing ack from a
  stalled one.

These only emit on the failure path, so they add no happy-path noise while
providing the breadcrumb that made this class of hang diagnosable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A single tool call in the Copilot CLI path calls trackEdit once per file
with the same tool call id, but ExternalEditTracker keyed _ongoingEdits by
edit key alone. The second file overwrote the first file's map entry,
stranding its deferred: the first file's core externalEdit proceed callback
never returned, so stopExternalEdits was never sent and core's per-resource
streaming-edit lock leaked. Every subsequent write to that same file then
hung forever in startExternalEdits (acquiring-locks), masked only by the
10s acknowledgment timeout (see microsoft#320292).

Store a list of entries per edit key so each tracked file keeps its own
deferred and acknowledgment. completeEdit now completes and awaits every
file under the key, releasing all locks, and cancellation removes only the
affected file's entry. Add a regression test proving a single edit key that
tracks two files releases both edits on completeEdit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@stevenh stevenh force-pushed the agents/fix-permission-issues-analysis branch from e2b6e99 to 3148e15 Compare July 16, 2026 14:33
@stevenh stevenh requested a review from Copilot July 16, 2026 14:34

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.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment on lines 24 to 35
/**
* Tracks ongoing external edit operations for agent tools.
* Manages the lifecycle of external edits by coordinating with VS Code's
* externalEdit API to ensure proper tracking and attribution of file changes.
*/
interface IOngoingEdit {
complete: () => void;
onDidComplete: Thenable<string>;
dispose: () => void;
}

export class ExternalEditTracker {
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.

Agent Window: Applying patches hangs forever

3 participants