Repository navigation
Description
Description
A custom subagent with a restricted tool/permission set fails on free-tier models with OpenCode's free tier can only be used from within OpenCode, while built-in agents on the same free models in the same session succeed. A second custom subagent that allows bash fails identically, so a missing bash tool alone does not explain it. What correlates with failure is the restricted shape itself (small tools allowlist under deny-by-default permissions) vs the full toolset of the passing built-ins.
Plugins
none related (reproduced with plain custom-agent definitions; no plugin involved)
OpenCode version
2.0.27
Steps to reproduce
- Use any free-tier model.
- Define a custom subagent restricted to docs-only tools (read, glob, grep, edit, write or equivalent;
bashnot included) under deny-by-default permissions. - Delegate a task to it -> fails with the free-tier error.
- Define a second custom subagent with the same restrictions plus
bashallowed -> fails with the same error. - Delegate a comparable task to a built-in agent with the full toolset in the same session -> succeeds.
Screenshot and/or share link
n/a
Operating System
Linux 6.18.33.2-microsoft-standard-WSL2 x86_64
Terminal
xterm-256color over WSL2, /bin/bash
Additional Context
Same-session A/B on the same free-tier models:
| agent | tool shape | result |
|---|---|---|
| built-in (full toolset) | all tools allowed | pass |
| custom subagent, docs-only allowlist (no bash) | 7 tools under *: deny |
403 FreeTierError |
| custom subagent, docs-only allowlist + bash | 8 tools incl. bash+read under *: deny |
403 FreeTierError |
Server log (role=server), both custom-subagent child sessions:
Failed to drain Session ... AI.Error.Authentication: OpenCode's free tier can only be used from within OpenCode
Reproducible every time (not intermittent). No workaround found that keeps the free tier short of granting the full toolset, which defeats the purpose of a restricted subagent.
Proven from source (anomalyco/opencode@v2.0.27, same version as here):
- The sentence exists nowhere: 0 matches for
can only be used from within OpenCode/within OpenCodein the whole repo tree (tag +dev) and 0 in the shipped~/.opencode/bin/opencodebinary. It is generated server-side. - The client's only role:
packages/ai/src/route/executor.ts:90-141takes the 401/403 response body verbatim as the message;packages/ai/src/provider-error.ts:267-273maps 401/403 toAuthenticationError(packages/ai/src/schema/errors.ts:73-76, theAI.Error.Authenticationtag in the log);packages/core/src/session/to-session-error.ts:33-34passes it through untouched. - Denied tools never reach the wire:
packages/core/src/tool.ts:225-233skipswhollyDisabledtools insnapshot(tool.ts:292-295), and the request body is built from that snapshot (packages/core/src/session/model-request.ts:298, serialized inpackages/ai/src/protocols/openai-chat.ts:832-838). So a restricted agent literally sends a smallertoolsarray. - The client sends only metadata headers (
x-opencode-session-id, parent session ids,x-opencode-project,x-opencode-session,x-opencode-client,User-Agent—model-request.ts:281-292) and performs no entitlement check: noisWithin*gate exists client-side, andx-opencode-requestis only read server-side (packages/console/app/src/lib/inference-proxy.ts:93-94).
Suggested fix: gate on x-opencode-session-id / x-opencode-parent-session-id (which the client already sends and which prove the call originates from a real OpenCode session tree) instead of on the shape of the tools array.
Related (same error cluster, linked to help triage — existing reports converge on "missing bash" as the trigger, but the bash-allowing case above contradicts that): #51241, #50627, #51193, #49723, #50806, #51612, #52880, #49680.
Activity
opencode-agent commentedon Oct 12, 2026
Thanks for the careful A/B and source trace. I think this is a duplicate of #50627. It's the same server-side free-tier check, which rejects a request when its
toolsarray is missing tools the gateway expects. In #50627 the captured requests differ only in their tools list, and #51241 shows that denyingread(not onlybash) also triggers it. So your finding fits: the check isn't only aboutbash, and an 8-tool allowlist still fails even withbashallowed. Please add your bash-allowed result to #50627, where it will help narrow down the gateway check.Possible duplicates and related issues, ordered by likelihood:
shellon a custom agent breaks the free tier; wire captures show only thetoolsarray differsshellorreadpermissions are denied #51241: free models fail whenshellorreadis denied (more than just bash)exploresubagent fails whilegeneralworks on the same modelsopencode/xxxxx-modelfails when used by a custom subagent #51612: custom subagent on a free model fails with the same errorA workaround reported in #50627, which I haven't verified: deny a tool with
"*": "deny"plus one harmless allow pattern (e.g."echo noop*": "allow") instead of a flat deny. The tool then stays in the request while staying effectively blocked. This may also work for the other tools your restricted subagent leaves out.The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different.
w3struk commentedon Oct 12, 2026
Not the same as #50627: there the failing requests are missing bash; here an 8-tool allowlist including bash+read still 403s on the same models where full-toolset agents pass in the same session — so the gateway check can't be "bash missing", it rejects small allowlists as such. This issue also adds the client-side source trace (v2.0.27: sentence is server-generated, denied tools never reach the wire) plus a concrete fix proposal (gate on x-opencode-session-id). Keeping the bash-allowed datapoint here as the counterexample to the bash-only theory.
opencode-agent commentedon Oct 12, 2026
Thanks for the follow-up. You're right that your bash-allowed result rules out the narrow "bash missing" theory that some commenters in #50627 hold. But the problem itself looks the same to me. The thread in #50627 (and #51241) had already concluded that the gateway rejects requests based on which tools are in the
toolsarray, not on bash specifically:shellorreadpermissions are denied #51241 shows that denyingread(not onlyshell) triggers the same 403.Your finding that an 8-tool allowlist including
bashandreadstill fails is the same server-side check: it wants more of the default toolset than those two. That's a sharper description of the existing bug, not a separate one. Your client-side trace and the suggestion to gate onx-opencode-session-id/x-opencode-parent-session-idwould be most useful in #50627, where the people tracking this cluster will see them next to the wire captures. Splitting them into a new issue spreads the same root cause across more threads.So I'm keeping this marked as a duplicate of #50627. Maintainers can still reopen or keep it separate if they see it differently. In the meantime, a workaround reported in #50627 (I haven't verified it) may help: instead of leaving tools out, keep them in the agent's toolset with a pattern rule such as
"*": "deny"plus one harmless allow pattern. They are still sent in the request but stay effectively blocked.opencode-agent commentedon Oct 13, 2026
Closing this as a duplicate of #50627, where this free-tier 403 is tracked. Your bash-allowed result fits what that thread found: the gateway decides based on which tools are in the request's
toolsarray, so dropping tools other thanbash(e.g.readin #51241) also triggers the error. Please add your 8-tool counterexample, the v2.0.27 client trace and the suggestion to checkx-opencode-session-id/x-opencode-parent-session-idinstead to #50627, where the people looking into this will see them alongside the wire captures. If maintainers see this as a separate problem, they can reopen it. Thanks for the detailed investigation.