Skip to content

fix: queue concurrent permission requests to prevent silent cancellations#2

Open
robertpyke wants to merge 3 commits intoformulahendry:mainfrom
robertpyke:fix/queue-permission-requests
Open

fix: queue concurrent permission requests to prevent silent cancellations#2
robertpyke wants to merge 3 commits intoformulahendry:mainfrom
robertpyke:fix/queue-permission-requests

Conversation

@robertpyke
Copy link

@robertpyke robertpyke commented Feb 24, 2026

I threw together a quick PR after hitting issues with multiple concurrent tool calls breaking the permission prompting. No issue if you throw this out, and do your own thing, but this seems to have fixed the bug for me locally, so I turned it into a PR. More detail below.

Problem

When multiple permission requests arrive simultaneously (e.g. parallel file reads + terminal command), each showQuickPick call dismisses the previous one, causing it to silently return undefined (treated as cancelled). The agent sees the tool call as rejected and re-asks on the next prompt, creating a loop where the user keeps approving but nothing executes.

Fix

Added a serial promise queue to PermissionHandler so concurrent requestPermission calls are processed one at a time. Each QuickPick waits for the previous one to resolve before showing.

Changes

  • src/handlers/PermissionHandler.ts — Added queue field, extracted logic into private handlePermission
  • CHANGELOG.md — Added 0.1.3 entry
  • package.json — Version bump to 0.1.3

…ions

When multiple permission requests arrived simultaneously, each showQuickPick
call would dismiss the previous one, silently returning 'cancelled'. This
caused allow_always selections to appear broken — the agent would keep
re-asking for permissions that the user had already approved.

Added a serial promise queue to PermissionHandler so concurrent requests
are shown one at a time.
Copy link

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

This PR addresses a VS Code UI race where concurrent permission prompts can dismiss each other, causing silent cancellations and repeated permission loops. It does so by serializing permission requests so only one QuickPick is shown at a time.

Changes:

  • Queue concurrent requestPermission calls in PermissionHandler and move the prompt logic into a private helper.
  • Bump extension version to 0.1.3.
  • Add a 0.1.3 changelog entry documenting the fix.

Reviewed changes

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

File Description
src/handlers/PermissionHandler.ts Serializes permission prompts to prevent overlapping QuickPick dialogs.
package.json Bumps extension version to 0.1.3.
CHANGELOG.md Documents the 0.1.3 fix for concurrent permission prompt clobbering.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"displayName": "ACP Client",
"description": "Agent Client Protocol client for VS Code — connect to GitHub Copilot, Claude Code, Gemini CLI, Qwen Code, Codex CLI, OpenCode, and any ACP-compatible AI coding agent",
"version": "0.1.2",
"version": "0.1.3",
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

The extension version was bumped here, but package-lock.json still has the root version set to 0.1.2. If this repo commits the lockfile, it should be updated to keep the metadata consistent (otherwise tooling that inspects the lockfile can report the old version).

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

@copilot open a new pull request to apply changes based on this feedback

robertpyke and others added 2 commits February 25, 2026 13:37
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@robertpyke
Copy link
Author

@copilot open a new pull request to apply changes based on the comments in this thread

Copy link

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

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


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +16 to +21
const result = this.queue.then(() => this.handlePermission(params));
this.queue = result.then(
() => undefined,
() => undefined,
);
return result.catch(() => ({ outcome: { outcome: 'cancelled' as const } }));
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

requestPermission converts any thrown error from handlePermission into a { outcome: 'cancelled' } response without logging or emitting a corresponding permission/responded telemetry event. This can mask real failures (e.g., showQuickPick/telemetry errors) as user cancellations and makes troubleshooting/metrics inaccurate. Consider logging the error (e.g., via logError) and reporting it via sendError/sendException, and decide whether to rethrow or return a distinct failure outcome rather than treating it as a user cancel.

Suggested change
const result = this.queue.then(() => this.handlePermission(params));
this.queue = result.then(
() => undefined,
() => undefined,
);
return result.catch(() => ({ outcome: { outcome: 'cancelled' as const } }));
const title = params.toolCall?.title || 'Permission Request';
const result = this.queue.then(() => this.handlePermission(params));
this.queue = result.then(
() => undefined,
() => undefined,
);
return result.catch(error => {
const message =
error instanceof Error
? `${error.name}: ${error.message}`
: `Unknown error: ${String(error)}`;
log(`requestPermission failed for "${title}": ${message}`);
sendEvent('permission/responded', {
permissionType: title,
outcome: 'error',
});
throw error;
});

Copilot uses AI. Check for mistakes.
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.

2 participants