Skip to content

Duplicate equivalent external_directory permission requests are emitted for concurrent tool calls #36055

Closed as not planned

Description

@hcsum

Description

OpenCode can emit multiple equivalent permission.asked events for the same external directory access when concurrent tool calls request access to the same directory.

This is visible in external clients that render each permission.asked event independently. For example, a Telegram bot client receives two separate permission prompts with the same permission, patterns, and always values, but different request IDs.

The built-in TUI does not show the duplicates simultaneously because it renders one queued blocker at a time. However, the duplicate pending requests still exist: with Allow once, the user may be prompted for the same effective permission again; with Allow always, the second matching request may be auto-resolved internally.

Observed behavior

For a single assistant turn, OpenCode may emit two events like:

{
  "type": "permission.asked",
  "properties": {
    "id": "per_...",
    "sessionID": "ses_...",
    "permission": "external_directory",
    "patterns": ["/path/to/sibling/*"],
    "always": ["/path/to/sibling/*"],
    "tool": {
      "messageID": "msg_...",
      "callID": "call_read"
    }
  }
}

and:

{
  "type": "permission.asked",
  "properties": {
    "id": "per_...",
    "sessionID": "ses_...",
    "permission": "external_directory",
    "patterns": ["/path/to/sibling/*"],
    "always": ["/path/to/sibling/*"],
    "tool": {
      "messageID": "msg_...",
      "callID": "call_glob"
    }
  }
}

The requests are semantically equivalent for directory access, but both remain pending/visible to clients that render permission events independently.

In a Telegram client, the user-visible sequence is:

The user sees two identical permission prompts for the same external_directory access.

Image

The user clicks always on the first prompt.
The user sends another message to continue the session.
The client/session still reports that it cannot continue because another permission needs to be approved.

Image

The user clicks always on the second, identical prompt.
That second approval fails with Permission request not found.

Image

After that, the session can continue normally, but the UX is confusing because the UI first claims a second approval is still required, then says that approval target no longer exists.

Why this happens

Each tool call gets its own permission context in session/tools.ts, including its own tool.callID.

Tools such as read, glob, and grep independently call assertExternalDirectoryEffect(...). When two concurrent tool calls access the same external directory, each tool asks for the same external_directory permission.

Permission.ask stores pending requests by request ID and does not coalesce equivalent pending requests.

If the user replies always to the first request, OpenCode adds an allow rule and automatically resolves the second matching pending request internally. External clients that already rendered the second prompt can still expose it as an apparent blocker, and replying to that stale prompt then fails because the request no longer exists.

Expected behavior

OpenCode should avoid surfacing duplicate equivalent permission requests for the same session and same effective permission scope.

Possible approaches:

  • Coalesce equivalent pending permission requests in the permission service.
  • Emit only one permission.asked event for equivalent pending requests and fan out the reply internally.
  • Or include grouping/coalescing metadata so external clients can safely render a single prompt and clear stale duplicates.

Notes

This is not specific to a Telegram client. The Telegram client only makes the duplicate events obvious because it renders each permission.asked as a separate message. The underlying duplicate requests originate in OpenCode.

Activity

  1. github-actions commented on Jul 9, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  2. github-actions commented on Sep 8, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions