You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Sub-sub-agents (depth: 2) "ask" permission doesn't surface to user and stalls #39112
When configuring OpenCode settings "subagent_depth" to 2, allowing subagents to deploy subagents at one level below, if a cascaded subagent tries to access any permission that is configured to "ask", the permission request never surfaces up to the user and the session stalls.
This can be easily tested with native "general" subagent by giving them access to deploy "explore" subagents.
Plugins
No response
OpenCode version
1.18.7
Steps to reproduce
Setup your opencode.json settings with subagent depth 2 and grant a subagent like general with task access to deploy explore subagents:
If your issue is distinct (e.g. specifically triggered by subagent_depth: 2 config or the two-level nesting case), please clarify how it differs from the above.
children() collects direct children only — one level, not a recursive descendant walk. Viewed from the root it holds level-1 sessions; a level-2 session id is never in that list, so its entry in sync.data.permission[...] is never read.
permissions() returns [] whenever session()?.parentID is truthy, i.e. from any non-root view — so navigating into the subagent view does not help either.
Net effect: a permission raised at depth 2 is unreachable from every view. Depth 1 works only because a level-1 session happens to be a direct child of the root.
The request itself is fine. packages/tui/src/context/sync.tsx stores it correctly under its own session id (setStore("permission", request.sessionID, [request])), and the event is broadcast per directory/workspace with no session filter. It is purely the render layer that drops it.
Worth noting that PermissionV1.Request (packages/schema/src/v1/permission.ts) carries only sessionID — no parentID or rootID — so the view has nothing to walk up with even if it wanted to.
It hangs rather than failing
packages/opencode/src/permission/index.ts blocks on Deferred.await(deferred) with no timeout anywhere in that file. The only thing that ever resolves it is the instance-state finalizer on teardown. So the call does not fail, it hangs until the session is killed — which is why blocked (1) in the status bar is the only visible symptom.
Please don't fix only permissions()
Three lines below, questions() has the identical short-circuit and the same one-level children():
#30635 was closed having fixed a single level of nesting, which is why this issue, #43996 and #44747 are all still open. A complete fix needs three things together: make children() walk descendants recursively, drop the non-root bail-out, and cover questions() in the same patch. I filed the questions() half as #49342 so it stays trackable — it is the same root cause, not a separate bug.
Our repro
Three levels, external_directory left at the default ask: an orchestrator agent delegates to a general-purpose subagent, which delegates to a read-only explorer subagent. The explorer globs a directory outside the worktree. The status bar shows blocked (1), no dialog renders in any view, and the turn never ends. Our configuration ships subagent_depth: 10, so depth-2 chains are routine rather than exotic — which is probably why we hit this on day one of using them.
As a workaround we have had to set external_directory to allow with an explicit deny floor over sensitive directories, because the alternative is pre-granting every external path a user might ever touch. We would much rather go back to ask.
One detail for whoever writes the fix: external_directory asks about path.join(path.dirname(target), "*") (packages/opencode/src/tool/external-directory.ts), never the file path — so a deny floor can only ever be directory-granular. That is fine, but it surprised us, and it may be worth a line in the permission docs.
Activity
github-actions commentedon Jul 27, 2026
This issue might be a duplicate of existing issues. Please check:
If your issue is distinct (e.g. specifically triggered by
subagent_depth: 2config or the two-level nesting case), please clarify how it differs from the above.balerdis commentedon Sep 16, 2026
Still reproduces on 1.18.31 (this was filed against 1.18.7). Source reading below in case it saves someone the trip.
Where it breaks
packages/tui/src/routes/session/index.tsx:Two defects that compound:
children()collects direct children only — one level, not a recursive descendant walk. Viewed from the root it holds level-1 sessions; a level-2 session id is never in that list, so its entry insync.data.permission[...]is never read.permissions()returns[]wheneversession()?.parentIDis truthy, i.e. from any non-root view — so navigating into the subagent view does not help either.Net effect: a permission raised at depth 2 is unreachable from every view. Depth 1 works only because a level-1 session happens to be a direct child of the root.
The request itself is fine.
packages/tui/src/context/sync.tsxstores it correctly under its own session id (setStore("permission", request.sessionID, [request])), and the event is broadcast per directory/workspace with no session filter. It is purely the render layer that drops it.Worth noting that
PermissionV1.Request(packages/schema/src/v1/permission.ts) carries onlysessionID— noparentIDorrootID— so the view has nothing to walk up with even if it wanted to.It hangs rather than failing
packages/opencode/src/permission/index.tsblocks onDeferred.await(deferred)with no timeout anywhere in that file. The only thing that ever resolves it is the instance-state finalizer on teardown. So the call does not fail, it hangs until the session is killed — which is whyblocked (1)in the status bar is the only visible symptom.Please don't fix only
permissions()Three lines below,
questions()has the identical short-circuit and the same one-levelchildren():#30635 was closed having fixed a single level of nesting, which is why this issue, #43996 and #44747 are all still open. A complete fix needs three things together: make
children()walk descendants recursively, drop the non-root bail-out, and coverquestions()in the same patch. I filed thequestions()half as #49342 so it stays trackable — it is the same root cause, not a separate bug.Our repro
Three levels,
external_directoryleft at the defaultask: an orchestrator agent delegates to a general-purpose subagent, which delegates to a read-only explorer subagent. The explorer globs a directory outside the worktree. The status bar showsblocked (1), no dialog renders in any view, and the turn never ends. Our configuration shipssubagent_depth: 10, so depth-2 chains are routine rather than exotic — which is probably why we hit this on day one of using them.As a workaround we have had to set
external_directorytoallowwith an explicitdenyfloor over sensitive directories, because the alternative is pre-granting every external path a user might ever touch. We would much rather go back toask.One detail for whoever writes the fix:
external_directoryasks aboutpath.join(path.dirname(target), "*")(packages/opencode/src/tool/external-directory.ts), never the file path — so adenyfloor can only ever be directory-granular. That is fine, but it surprised us, and it may be worth a line in the permission docs.thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊