Repository navigation
Closed as not planned
Closed as not planned
Copy link
Description
Summary
Permission asks raised by MCP tools invoked from inside Code Mode (execute) never render in the TUI. The execute call blocks indefinitely on the invisible ask, and the next user message (or Esc) aborts it with {"type":"aborted","message":"Tool execution interrupted"}. This silently deadlocks agent↔user workflows. Direct (non-Code-Mode) tool calls show their permission dialogs normally.
Environment
- opencode version: 2.0.16
- OS: Linux 7.2.6-1-omarchy-rc3 (x64, Arch-based Omarchy)
- Terminal: ghostty (TERM=xterm-ghostty, COLORTERM=truecolor)
- Shell: /bin/bash
- Install/channel: latest (v2.0.16, background service via
opencode serve --service) - Active plugins: superpowers (git+https://github.com/obra/superpowers.git)
- MCP: two local stdio servers (a custom FastMCP Python server, desktop-commander)
Reproduction
- Configure an MCP tool to require an ask, e.g. in
opencode.json:
{ "permissions": [ { "action": "hyprland_*", "resource": "*", "effect": "ask" } ] } - In a session, call that tool from Code Mode:
await tools.hyprland.clipboard_copy({ text: "hello" }) - No permission dialog appears anywhere in the TUI.
- The
executecall hangs indefinitely (observed 5–44 minutes). - Type any new message (or press Esc) → the tool result becomes:
{"error":{"type":"aborted","message":"Tool execution interrupted"}}
Expected Behavior
The permission ask renders in the TUI like asks for direct tool calls, so the user can approve/deny and the Code Mode call proceeds.
Actual Behavior
- No ask is ever displayed; the model believes the tool call is running while the user sees nothing. Both sides wait on each other.
- A pending ask eventually exceeds the MCP request timeout in some cases: on v1.18.32→v2 era data we also captured
MCP error -32001: Request timed out(~60 s) for an unanswered ask. - Additional quirks observed in the same timeline (may be the same root cause):
- Editing
opencode.jsonpermissions mid-session firesconfig.updated(seen in the log), but the running session still hangs the newly-allowed tool — permission rules are only effective for sessions started after the change. - An "Allow always" answer is stored project-scoped (row in the
permissiontable with the project id). A long-running session that belongs to the legacyglobalproject never receives that grant, so its Code Mode calls keep asking (and hanging) forever.
- Editing
Additional Context
Evidence that the hang is the invisible ask (not the MCP server):
- A config-
allowed tool called via Code Mode returns in 31 ms (tools.hyprland.list_windows()), so Code Mode → MCP plumbing and permission allow-rules work. - A hung call completed at 18:28:53 — the exact timestamp an
hyprland_clipboard_copyallow row was written to thepermissiontable — i.e. it had been blocked on the ask for 278 s and was released by the grant. - Reproduced twice in one session after a config change was confirmed applied (
config.updatedin the server log 4 s before the second hang). - The server log shows
message=askingentries for direct tool-call asks (which users answer normally), but no asking entries for the Code Mode hangs.
Full investigation report with timeline available on request.
Reacted by Markus Dietrich
Metadata
Metadata
Assignees
Labels
No labels
Activity
LeoParkerOu commentedon Sep 28, 2026
I'd like to take this on. I'll first reproduce the missing permission prompt with a minimal local MCP server, then trace the Code Mode permission request through to the TUI and work on a focused fix with regression coverage.
If anyone is already working on this, please let me know so we can avoid duplicating work.
rissanssi commentedon Sep 28, 2026
Not aware of anyone working on it — go ahead. Two adjacent observations from the original investigation that may save you time: (1) permission changes in
opencode.jsonfireconfig.updatedbut don't apply to already-running sessions; (2) "Allow always" grants are stored project-scoped and didn't cover a session belonging to the legacyglobalproject. Sanity check for your repro: a config-allowed tool via Code Mode returns in ~31 ms, so the plumbing is fine — it's specifically theaskpath that never surfaces. @argszero did source-level analysis of the same pipeline over in #51224 (TUI renders onlylist()?.[0]per session), which may be relevant context.holny commentedon Sep 29, 2026
I'll take this one (the sibling of #51224 — asks never surface at all vs parallel asks orphaning, two separate defects). Code Mode executes MCP tools inside the
executesandbox, and a permission ask raised there has no path back to the TUI's permission surface, so the call parks forever until abort.Plan: trace the permission ask raised from inside the Code Mode execution context through to where session-scope asks get surfaced, find where the MCP-server scope breaks the routing, and fix the routing so the ask reaches the TUI like a direct tool call does. Covered by a test that invokes an MCP tool via Code Mode with the permission gate on and asserts the ask is delivered.
Fixes #51223
LeoParkerOu commentedon Sep 30, 2026
Thanks for the additional context. I have now tested the complete SessionRunner → Code Mode → stdio MCP → server/SSE → TUI → HTTP approval flow, including on v2.0.16.
Two failures reproduce locally: a delayed empty permission-list response can erase a newer ask from the UI, and changing the config from ask to allow does not release an already-pending request even after the agent rules refresh. I have local fixes and regression tests for both. The same-location legacy-global session also successfully reuses an “always” grant in my test.
However, the ordinary single-ask path works on unmodified v2.0.16 in my setup, so I cannot yet establish that these two failures explain the entire original report. Could you share the investigation report you mentioned, especially the minimal MCP/plugin configuration and the sequence involving the legacy-global session? Please redact credentials and private paths. The useful details are whether permission.asked is emitted, whether GET /api/session/{id}/permission still contains the request while the TUI is stuck, and whether a permission-list response arrives after the asked event.
I would like to verify the original failure before claiming that #51223 is fully fixed.
rissanssi commentedon Oct 3, 2026
Heads-up: @holny also claimed this on Sep 29 ("Fixes #51223"), so you two may want to coordinate to avoid duplicate PRs.
To your questions — config and sequence, private paths redacted:
Minimal config: one local stdio MCP server + wildcard ask rule with a few specific allow overrides. The failing call was
clipboard_copyinvoked from Code Mode'sexecutesandbox:Legacy-global sequence (all UTC, DB-verified): session created Sep 16 14:32 with
project_id = global(the legacy project with an empty name). On Sep 23 14:01 a new project row was created for the same directory, and on Sep 24 18:28:53 an "always allow" grant forhyprland_clipboard_copywas written under that project — so it never matched the session'sglobalscoping. Timeline: two parallelexecute→ MCP calls at 18:24:15; #1 completed at 18:28:53 (exactly when the grant row was written, 278 s); #2 was orphaned and aborted at 18:48:39 (1462 s); subsequent single calls hung 297 s and 938 s, each aborting to the second with the user's next message.On your three instrumentation questions: I have to be honest that none were captured live — the persisted event log only contains
message.part.updated/message.updated/session.updated, so permission events aren't recoverable retroactively, and I didn't queryGET /permissionwhile a call was stuck. Indirect evidence that the ask was live server-side: call #1 resolved to the second exactly when the grant row appeared, i.e. a pending ask existed and a grant could satisfy it. One more data point you may find useful: on Sep 16 (pre-migration, same root cause), the same invisible ask surfaced instead asMCP error -32001: Request timed outafter the 60 s MCP timeout — so in that code path the ask was neither surfaced nor indefinitely parked.Happy to pull exact timestamps or part records for any specific call if useful.
holny commentedon Oct 4, 2026
Apologies — I missed this thread was already claimed when I picked it up. Withdrawing; @LeoParkerOu's approach (live permission updates during list refresh) sounds like the right layer to fix this from. One finding from my pass that might be useful either way: the server side is clean —
Permission.Askedpublishes correctly from inside the Code Mode sandbox with the full session/action/tool source intact, so the drop is purely client-side rendering.rissanssi commentedon Oct 6, 2026
Thanks @holny — withdrawal noted, and that's a valuable data point:
Permission.Askedpublishing correctly from inside the sandbox confirms the ask is live server-side (which matches the indirect evidence in my timeline above) and localizes the defect to the client, exactly where @LeoParkerOu's fix is aimed.opencodejr commentedon Oct 28, 2026
Thanks for the detailed timeline. I tried to reproduce this on 2.0.16 and couldn't.
What I ran:
hyprlandthat exposesclipboard_copy.{ "action": "hyprland_*", "resource": "*", "effect": "ask" }.executewithawait tools.hyprland.clipboard_copy({ text: "hello" }).The TUI showed Permission required · Call tool hyprland_clipboard_copy (Allow once / Always allow / Reject) within a few seconds. After Allow once, the
executecall completed normally. A similar attempt on 2.0.18 in #51504 also showed the prompt every time.So a fresh session doesn't seem to be enough to trigger it. These would help narrow it down:
globalproject.executecalls, or another permission prompt already pending in the same turn? See permissions: parallel Code Mode asks for the same tool orphan the second request #51224.opencode.jsonand the server log lines around one hang.Related issues, most likely first:
askrules not prompting (same area)rekram1-node commentedon Oct 29, 2026
Closing since this can't be reproduced on recent builds: the ask shows up in the TUI, and execute continues after approval. If you still hit it on the latest release, please reopen with the server log lines around a hang, and whether parallel asks or a subagent session were involved.
opencodejr commentedon Oct 31, 2026
I'm closing this for now because I couldn't reproduce it, and we haven't heard back on the follow-up questions. On 2.0.16, with a local stdio MCP server and your
hyprland_*askrule, the TUI showed the permission prompt for a Code Modeexecutecall. After approval the call completed normally.If it still happens for you, please reply with:
globalproject)executecalls were involvedWe can reopen it then. Related: #51224, #56907, #51504.