Skip to content

console: free-tier 403 rejects restricted subagents even with bash allowed #54744

Closed as duplicate

Description

@w3struk

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

  1. Use any free-tier model.
  2. Define a custom subagent restricted to docs-only tools (read, glob, grep, edit, write or equivalent; bash not included) under deny-by-default permissions.
  3. Delegate a task to it -> fails with the free-tier error.
  4. Define a second custom subagent with the same restrictions plus bash allowed -> fails with the same error.
  5. 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 OpenCode in the whole repo tree (tag + dev) and 0 in the shipped ~/.opencode/bin/opencode binary. It is generated server-side.
  • The client's only role: packages/ai/src/route/executor.ts:90-141 takes the 401/403 response body verbatim as the message; packages/ai/src/provider-error.ts:267-273 maps 401/403 to AuthenticationError (packages/ai/src/schema/errors.ts:73-76, the AI.Error.Authentication tag in the log); packages/core/src/session/to-session-error.ts:33-34 passes it through untouched.
  • Denied tools never reach the wire: packages/core/src/tool.ts:225-233 skips whollyDisabled tools in snapshot (tool.ts:292-295), and the request body is built from that snapshot (packages/core/src/session/model-request.ts:298, serialized in packages/ai/src/protocols/openai-chat.ts:832-838). So a restricted agent literally sends a smaller tools array.
  • 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: no isWithin* gate exists client-side, and x-opencode-request is 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

  1. opencode-agent commented on Oct 12, 2026

    @opencode-agent
    Contributor

    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 tools array is missing tools the gateway expects. In #50627 the captured requests differ only in their tools list, and #51241 shows that denying read (not only bash) also triggers it. So your finding fits: the check isn't only about bash, and an 8-tool allowlist still fails even with bash allowed. 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:

    A 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.

  2. w3struk commented on Oct 12, 2026

    @w3struk
    Author

    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.

  3. opencode-agent commented on Oct 12, 2026

    @opencode-agent
    Contributor

    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 tools array, not on bash specifically:

    Your finding that an 8-tool allowlist including bash and read still 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 on x-opencode-session-id / x-opencode-parent-session-id would 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.

  4. opencode-agent commented on Oct 13, 2026

    @opencode-agent
    Contributor

    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 tools array, so dropping tools other than bash (e.g. read in #51241) also triggers the error. Please add your 8-tool counterexample, the v2.0.27 client trace and the suggestion to check x-opencode-session-id / x-opencode-parent-session-id instead 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.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions