policy: deny shell * on custom agent breaks free tier with "can only be used from within OpenCode" · Issue #50627 · anomalyco/opencode · GitHub
Skip to content

policy: deny shell * on custom agent breaks free tier with "can only be used from within OpenCode" #50627

Closed as duplicate

Description

@Saka-CS

Summary

Enabling permissions: [{action: shell, resource: "*", effect: deny}] on a custom primary agent makes every request fail on all free-tier models with OpenCode's free tier can only be used from within OpenCode, even though the request originates from inside OpenCode TUI.

Environment

  • opencode version: v2.0.13
  • OS: Linux saka 7.2.5-3-omarchy feat: compact and other improvements #1 SMP PREEMPT_DYNAMIC Mon, 14 Sep 2026 19:55:01 +0000 x86_64 GNU/Linux
  • Terminal: TERM=xterm-256color, TERM_PROGRAM=ghostty, COLORTERM=truecolor
  • Shell: /usr/bin/bash
  • Install/channel: latest
  • Active plugins: none found

Reproduction

  1. Use TUI with custom primary agent at ~/.config/opencode/agents/search.md with frontmatter:
permissions:
  - action: edit
    resource: "*"
    effect: deny
  - action: shell
    resource: "*"
    effect: deny
  - action: subagent
    resource: "*"
    effect: deny
  - action: subagent
    resource: "explore"
    effect: allow
  - action: subagent
    resource: "general"
    effect: allow
  - action: websearch
    resource: "*"
    effect: allow
  - action: webfetch
    resource: "*"
    effect: allow
  - action: read
    resource: "*"
    effect: allow
  - action: glob
    resource: "*"
    effect: allow
  - action: grep
    resource: "*"
    effect: allow
  1. Send any message to that agent on any Console free-tier model via TUI.
  2. Observe Error: Error from provider (Console): OpenCode's free tier can only be used from within OpenCode.
  3. Comment out the shell: "*" deny lines -> works again.

Expected Behavior

Shell deny should only block shell tool calls; the LLM request itself should still authenticate as in-OpenCode free-tier use.

Actual Behavior

Entire session fails with free-tier origin error on every message, before any tool use. Always reproducible; removal fixes. Affects all free models. Error is single line only.

Additional Context

Activity

  1. avaloner1109 commented on Sep 23, 2026

    @avaloner1109

    Additional evidence — I independently hit this exact trigger and isolated the mechanism. This is the same root cause as #50806 / #49588 (compaction) / #49756, and it is not specific to the permission frontmatter or to shell-* denial specifically: the gate keys off the absence of the bash tool in the request body.

    Minimal repro (no frontmatter needed)

    opencode.json:

    {
      "agent": {
        "t2": { "model": "opencode/big-pickle", "tools": { "bash": false } }
      }
    }
    • opencode run --agent t2 -m opencode/big-pickle "hi" → ❌ OpenCode's free tier can only be used from within OpenCode
    • Same agent with "tools": { "bash": false } removed → ✅ works
    • A different custom agent with a planner-style system prompt (full toolset) → ✅ works

    Environment: opencode 1.18.21 and 1.18.32 (repro'd on both, so it is not a version-string gate), Windows, npm global install, provider=opencode (Console), model big-pickle, both TUI and headless opencode run.

    Wire-level proof

    I pointed the Console provider at a local mock endpoint and captured the exact requests for the working vs failing agent. The two requests are byte-identical in every header — authorization, user-agent: opencode/1.18.32 ai-sdk/provider-utils/4.0.40 runtime/bun/1.3.14, x-opencode-client, x-opencode-project, x-opencode-session, x-opencode-request all match. The only difference is the request body:

    tools in request result
    working agent 35 tools, incl. bash ✅
    failing agent 34 tools, no bash ❌ FreeTierError

    Same binary, same account, same project, same model — a byte-level diff of the two HTTP requests reduces entirely to the tools array missing bash.

    Real-world impact

    Any agent that denies bash/shell is fully blocked on the free tier:

    Suggested direction

    The free-tier origin check should not depend on the request's tools array (any tool-restricted agent, including legitimate read-only/plan agents, gets a false "outside OpenCode" verdict). A signed session/ticket or the existing x-opencode-session/x-opencode-request headers validated server-side would be a more robust signal. Happy to provide the full capture if useful.

  2. ebzych commented on Sep 24, 2026

    @ebzych

    Same in v1.18.25 with

    permission:
      bash:
        "*": deny

    It worked in the past and works with ask and allow now.

  3. ruefenacht27 commented on Sep 29, 2026

    @ruefenacht27

    Confirming this on V1 as well — permission: block syntax rather than V2 permissions: — plus a bisect that narrows the trigger a bit further.

    Environment

    • opencode 1.18.15
    • Windows 11 x64 (NT 10.0.26200)
    • No plugins, no tools.* overrides in config
    • Provider: built-in opencode (Zen Console), free-tier models

    V1 repro

    Agent at ~/.config/opencode/agent/example.md:

    ---
    description: example
    mode: all
    steps: 30
    permission:
      read: allow
      glob: allow
      grep: allow
      websearch: allow
      webfetch: allow
      skill: allow
      edit: ask
      bash: deny
      task: allow
    ---

    Spawning it via the subagent tool fails immediately:

    AI.Error.Authentication: Error from provider (Console): OpenCode's free tier can only be used from within OpenCode
      at SessionStep.attempt (...) -> SessionRunner.runStep (...) -> SessionRunner.drain (...)
    

    Same agent, same parent session, same model, same task — the only variable changed between pass and fail is the bash line.

    Bisect

    Four agents differing only in the bash line, all mode: subagent, spawned in alternating order from one parent session:

    bash value Result
    key absent entirely works
    ask works
    deny fails

    Two things this adds on top of the V2 repro above:

    1. Omitting the bash key entirely also works — so the full permission block minus bash is fine, and no other key in the block is implicated. Only the allow -> deny transition trips it.
    2. bash: ask works. That suggests the gate may be reacting to tool removal rather than to shell permissions as such.

    Two scope notes that may help triage

    Root cause pointer

    #51315 appears to have identified the actual mechanism: the gate runs server-side on Zen and rejects requests whose tools array is missing read or carries 0-3 tools, and disabling edit/write/apply_patch is harmless. If that's right, the deny is changing the tools array in some way that trips the same check, and the fix belongs in the gate rather than in permission normalization.

    One suggestion on the error message itself: because it surfaces as AI.Error.Authentication and names billing, the natural first debugging step is to check the account and re-login, which is always a dead end here. An error that said "the configured permissions produced a request the free tier rejected" would save a lot of that.

    Workaround

    bash: ask instead of bash: deny. Confirmed working on 1.18.15 — the agent still cannot run shell commands without an explicit prompt, so most of the intended guardrail survives.

  4. Mike-Rice commented on Sep 30, 2026

    @Mike-Rice

    Confirming on V1 and built-in agents, plus a CLI-only minimal repro.

    Environment: opencode 1.18.33 (V1 plugin API), Linux; built-in opencode provider; free-tier model opencode/big-pickle; empty scratch project, no third-party plugins.

    opencode.json:

    {
      "$schema": "https://opencode.ai/config.json",
      "agent": {
        "bigplain":    { "model": "opencode/big-pickle" },
        "bigask":      { "model": "opencode/big-pickle", "permission": { "bash": "ask" } },
        "bigdeny":     { "model": "opencode/big-pickle", "permission": { "bash": "deny" } },
        "bigtoolsoff": { "model": "opencode/big-pickle", "tools": { "bash": false } },
        "plan":        { "model": "opencode/big-pickle", "permission": { "edit": "deny", "bash": "deny" } }
      }
    }

    opencode run --agent <name> -m opencode/big-pickle "reply with the single word: hi":

    • bigplain → hi (exit 0)
    • bigask → hi (exit 0)
    • bigdeny → Error: OpenCode's free tier can only be used from within OpenCode (exit 1)
    • bigtoolsoff → same error (exit 1)
    • plan (built-in) → same error (exit 1)

    So it is not shell-* enforcement per se — merely withholding the bash tool from the outgoing request makes the gate reject, exactly as #51315 describes for tools.read. ask/allow keep bash present and work. This also reproduces for a built-in agent (plan with permission.bash: "deny"), no custom frontmatter required.

    Workaround: permission.bash: "ask" (the tool is advertised; execution still requires approval), or a paid Go model.

    Suggested fix: keep bash in the request schema while denying execution locally, or make the error message state the bash-tool requirement.

  5. hexxmate commented on Oct 2, 2026

    @hexxmate

    WORKAROUND for this issue

    Reproduced on Opencode V 1.18.34. Reporting a finding that may help with this and related free-tier/tool-availability issues (looks related to #50081, possibly #51315 and #49723 , #49680 too, though I haven't tested those specifically).

    Root cause (as far as I can tell): The free-tier rejection isn't tied to the deny value itself — it's triggered when a tool is removed from the model's tool list entirely.

    Denying with a flat tool: deny drops the tool from the list and trips the "can only be used from within OpenCode" error.

    Keeping the tool present in the list — even if every actual command/action is denied — avoids it.

    Minimal repro (bash):

    permission:
      edit: allow
      bash: deny

    → Error from provider (Console): OpenCode's free tier can only be used from within OpenCode

    permission:
      edit: allow
      bash:
        "*": "deny"
        "ls *": "allow"

    → Works. Bash tool stays visible to the model, but only ls * is actually permitted — everything else is denied.

    Generalizing this: this is probably not bash-specific. My guess is the free tier is checking "is the tool present," not "what can it do," so the same trick should apply to any gated tool: instead of a flat deny, give it a pattern-based permission block with "*": "deny" and one narrow, basically-useless allow rule, just to keep the tool visible without actually handing it any real power.

    For bash, ls * works well since it's read-only and side-effect-free. For tools like edit or write, there may not be an equally harmless real carve-out — you may need to scope the allow-rule to a path pattern that can't realistically match anything (e.g. a fixed nonexistent file path) just to satisfy the presence check.

  6. yturkkan commented on Oct 2, 2026

    @yturkkan

    Proposed update to #50627

    Update: Retested on Desktop-managed service 2.0.22 after updating. Six fresh
    build-session requests (two models × three permission configurations) reproduce
    the same results: default succeeds, edit deny + shell deny fails with the same
    403 FreeTierError, edit deny + shell ask succeeds. No tools were called. The
    update did not resolve this reproduction; agent configuration was not changed.

    Suggested title if a separate issue is requested: policy: shell deny rejects Spark and MiMo free requests in V2 Desktop 2.0.21

    Summary

    Confirming this permission-dependent free-tier rejection on OpenCode V2 Desktop
    2.0.21 with both MiMo Flash Free and Muse Spark Contributor Free. On the same
    Desktop-managed service, changing only the shell permission from default to
    deny changes a successful reply into HTTP 403 FreeTierError.

    Environment

    • opencode version: Desktop-bundled CLI and shared service 2.0.21; separate PATH CLI 2.0.18
    • OS: macOS / Darwin 27.0.0, arm64
    • Terminal: Desktop; diagnostic shell TERM=xterm-256color, TERM_PROGRAM/COLORTERM unavailable
    • Shell: /bin/zsh
    • Install/channel: official installed Desktop; latest release channel reported by runtime diagnostics
    • Active plugins: @hueyexe/opencode-ensemble@0.18.0 and a local Panel plugin. Not reproduced with plugins disabled. All comparison tests used the built-in build agent, not Panel agents.

    Reproduction

    1. Start the official Desktop application and use its managed service.
    2. Create a fresh built-in build session via POST /api/session with an explicit model reference and the active project location. No custom session permissions. Test separately with:
      • {"providerID":"opencode","id":"mimo-v2.6-flash-free"}
      • {"providerID":"opencode","id":"muse-spark-1.3-contributor-free"}
    3. Submit a prompt via POST /api/session/{sessionID}/prompt: Connection test. Do not use tools, read or change files. Reply only OK. (Actual test prompt was the Turkish equivalent, requesting TAMAM.) Both models reply successfully.
    4. Repeat in a fresh session with only this added session permission:
      [{"action":"shell","resource":"*","effect":"deny"}]
    5. Both models fail before replying with the error below.
    6. Repeat with these permissions:
      [
        {"action":"edit","resource":"*","effect":"deny"},
        {"action":"shell","resource":"*","effect":"ask"}
      ]
      Both models reply successfully again. No shell execution was requested or approved.

    Expected Behavior

    Denying shell execution must not invalidate legitimate requests from OpenCode
    or prevent read-only reviewers from using the free-tier models.

    Actual Behavior

    Assistant context contains:

    {
      "type": "provider.auth",
      "message": "OpenCode's free tier can only be used from within OpenCode",
      "status": 403,
      "response": {
        "body": "{\"type\":\"error\",\"error\":{\"type\":\"FreeTierError\",\"message\":\"OpenCode's free tier can only be used from within OpenCode\"}}"
      }
    }

    Additional Context

    Same Desktop service, built-in build agent MiMo free Spark free
    Default shell permissions Successful reply Successful reply
    shell * deny 403 FreeTierError 403 FreeTierError
    shell * ask + edit * deny Successful reply Successful reply

    Custom Panel reviewer definitions deny both edit and shell, which led us to
    this issue. An earlier all-tools-denied build test incorrectly suggested a
    Desktop identity problem; the controlled same-service tests above ruled that
    out as the explanation for this reproduction.

    The shared service is launched by official Desktop with OPENCODE_CLIENT=desktop.
    No provider headers, credentials or endpoints were modified, and no paid model
    was substituted. Independent official CLI tests also succeeded but are not
    needed to demonstrate the permission-only comparison above.

    The workaround changes a strict shell ban into approval-required execution;
    it is not an equivalent read-only security policy. edit deny alone does not
    prevent filesystem mutation through approved shell commands. Please fix the
    free-tier check rather than requiring users to weaken reviewer permissions.

    This is consistent with the request-toolset observations already reported in
    this issue. We did not capture outgoing HTTPS bodies or inspect the provider's
    server-side validation code, so we are not claiming wire-level proof of the
    specific tool-presence check.

  7. hymsoft commented on Oct 3, 2026

    @hymsoft

    Confirming the same trigger on my setup + minimal workaround.

    Setup:

    • OpenCode 1.18.34 (opencode --version)
    • Windows 11
    • Custom project subagent (mode: subagent) that never needs bash (only reads .md/.typ and returns text)

    Failing frontmatter (repro 3/3):

    permission:
      read:
        "*": deny
        "**/*.md": allow
        "*.md": allow
      edit:
        "*": deny
        "**/*.md": allow
        "*.md": allow
        ".opencode/**": deny
      bash: deny
      question: allow

    Error every time via task:

    OpenCode's free tier can only be used from within OpenCode

    Working frontmatter (repro 3/3 after restart):

    permission:
      read:
        "*": deny
        "**/*.md": allow
        "*.md": allow
      edit:
        "*": deny
        "**/*.md": allow
        "*.md": allow
        ".opencode/**": deny
      bash:
        "*": deny
        "echo maquetador-noop*": allow
      question: allow

    The agent never calls bash. Declaring a noop echo allow is enough to route correctly. This matches #52880 and #49723.
    Happy to add logs if a maintainer tells me which ones.

  8. oskar089 commented on Oct 8, 2026

    @oskar089

    Reproduced on v2.0.19 / Windows 10.0.26300 with a second config surface: agents defined inline in opencode.json ("mode": "subagent" with "permission": {"bash": "deny", "edit": "deny", "task": "deny", "write": "deny"}) — no agents/*.md frontmatter involved.

    Evidence (4/4 failures in one server run):

    • Two reviewer-lens task launches (agent: review-reliability, prompt materialized by the gentle-ai review relay) produced child sessions ses_ee2e63886ffe2sDW5dlxGou8QW and ses_ee2e54bb9ffeW6CEssRfn4YpKs that died with:

      level=ERROR message="Failed to drain Session"
      cause="AI.Error: OpenCode's free tier can only be used from within OpenCode ...
        [cause]: AI.Error.Authentication: OpenCode's free tier can only be used from within OpenCode"
      

      The caller surface was opencode_review_transport_relay_refused (reason: provider_failed).

    • Control probe: gentle-ai-explore (same inline permission block) with the prompt "Reply with exactly one line: PROBE-OK. Do not use any tools." → Subagent failed (sessionID: ses_ee2e02e8fffebzF8Vh9CO87CK9): OpenCode's free tier can only be used from within OpenCode.

    • The parent session in the same process on the identical free-tier model (opencode/big-pickle) works normally; only child sessions are rejected.

    • Same log has a precedent from 2026-10-04 (providerID=opencode modelID=big-pickle agent=gentle-ai-explore mode=subagent).

    This matches the control matrix in #51193 / #53908 (shell deny → fails, edit deny only → works), but the trigger here is the opencode.json inline permission schema, which the existing reports do not cover.

  9. w3struk commented on Oct 12, 2026

    @w3struk

    Cross-posting the datapoint from #54744 (marked duplicate of this issue) so it lives next to the wire captures:

    • An 8-tool allowlist including bash and read still 403s (OpenCode's free tier can only be used from within OpenCode) on free-tier models in the same session where full-toolset agents pass. So the gateway wants more of the default toolset than those two — consistent with hexxmate's conclusion that removing any gated tool trips the check.
    • Client-side trace at v2.0.27 (same version): the sentence exists nowhere in the repo tree or the shipped binary (0 matches) — it is server-generated. The client only takes the 401/403 body verbatim (packages/ai/src/route/executor.ts:90-141) and maps it to AI.Error.Authentication (packages/ai/src/provider-error.ts:267-273). Denied tools never reach the wire: Tool.snapshot skips whollyDisabled tools (packages/core/src/tool.ts:225-233,292-295); the body is built from that snapshot (packages/core/src/session/model-request.ts:298).
    • Concrete fix proposal: the client already sends x-opencode-session-id / x-opencode-parent-session-id (model-request.ts:281-292), which prove the call comes from a real OpenCode session tree. Gating on those instead of on the tools array shape would stop punishing legitimately restricted agents.
  10. gelnuro commented on Oct 12, 2026

    @gelnuro

    Deny-first matrix from #54882, since it may help pin down what the gateway actually requires.

    Setup: project config has exactly one permission rule — { "action": "*", "resource": "*", "effect": "deny" } — and the free-model agent is a subagent (mode: subagent, model: opencode/mimo-v2.6-flash-free), so this gate clearly applies to subagents spawned from inside OpenCode too, not only to primary agents. Version v2.0.26, Linux aarch64. Same trivial probe every time ("Reply with exactly PROBE-OK and nothing else. Do not call tools or modify files."), nothing else changed between runs.

    Agent permissions: Result
    none at all (inherits the pure deny) fails, free-tier error
    shell: "echo 'Hello!'" only 4/4 failures
    read: ".opencode/opencode.jsonc" + shell: "git log *" PROBE-OK repeatedly, some later failures
    read: "README.md" + shell: "echo 'Hello!'" PROBE-OK

    Two things worth noting:

    1. The README.md run confirms @opencode-agent's prediction above — the specific resource is irrelevant, and the config file itself does not need to be readable. Any read allow looks sufficient here; a shell allow on its own does not.
    2. This sits awkwardly next to the datapoint cross-posted from console: free-tier 403 rejects restricted subagents even with bash allowed #54744, where an 8-tool allowlist including bash and read still 403s. Here a two-rule allowlist (read + shell) passes. So it is not simply "the gateway wants most of the default tool set" — either tool identity matters more than count, or the intermittency seen in both threads is doing a lot of the work.

    The intermittency is real and worth separating from the permission question: with an unchanged allowlist that had passed many times, the same probe then failed 3 times in a row and later passed again. Any fix probably needs to address both the "denied tools are dropped from the request" behaviour and whatever makes an otherwise-eligible request flap.

  11. UnixUserDev commented on Oct 14, 2026

    @UnixUserDev

    Empirical results from a real-world repro (Windows 11 Insider build 10.0.26340, opencode v2.0.27, stock desktop install, model opencode/mimo-v2.6-flash-free), re: the deny→ask workaround:

    Setup: 9 custom agents in .opencode/agents/. Exactly one of them (planner) had bash: deny in its frontmatter permissions — the other 8 only have edit: deny. Every subagent spawn of planner failed 2/2 with:

    OpenCode's free tier can only be used from within OpenCode.
    Download it at https://opencode.ai and keep building.
    

    while the parent session on the same model worked normally.

    Tests, one change at a time:

    bash edit spawn result
    deny deny ❌ fails (2/2, exact error above)
    ask ask ✅ spawns + completes
    ask deny ✅ spawns + completes

    So in this case bash: deny was the trigger and edit: deny was harmless — consistent with #54744's finding that this isn't purely "any missing standard tool": the shell/bash tool appears to be the one whose absence trips the check most often, but an allowlist-only config can apparently still fail (the workspace-level config here was an allowlist including shell, and it worked fine — the failure only started at the agent-level bash: deny).

    Also explains the partial-failure pattern in #49723-style reports: only agents carrying the restrictive rule fail, so some agents work and some don't in the same install.

    Workaround confirmed: bash: deny → bash: ask on the offending custom agent. (Kept edit: deny to preserve the read-only intent of that agent — spawn still works.)

  12. cobrabr commented on Oct 15, 2026

    @cobrabr

    Adding one repro path that I don't see covered above: a plugin calling ctx.generate.text with no tools array at all, rather than an agent whose tool list got pruned by deny rules.

    Mechanism

    The plugin (https://github.com/aSempruch/opencode-auto-mode) issues its LLM reviewer call from a permission.evaluate hook:

    // src/index.ts
    const call = ctx.generate.text({ prompt, ...(model ? { model } : {}) }, { signal: controller.signal })

    The string tools does not appear anywhere in that file. The request body therefore carries an empty tool list, which is the maximal form of the "standard tools absent from the request" condition described by @avaloner1109, @hexxmate and @w3struk — there's no deny rule or allowlist involved at all.

    Evidence

    Same ctx.generate.text call, same process, same session, differing only in the model:

    model provider reviews result
    deepseek/deepseek-v4.1-flash openrouter 380 all succeed
    space-bunny-free opencode 1388 success, 41 error see below

    The openrouter model kept working after the free-tier failures had started — 55 successful generate.text calls on 2026-10-11 through 2026-10-14 — so the free-model 403s are not a general breakage of the generate API or of the session.

    Daily breakdown for space-bunny-free, counting only calls that actually reached the model:

    Date Success Error
    10-05 188 0
    10-06 169 0
    10-07 283 1
    10-08 278 1
    10-10 231 0
    10-11 239 2
    10-14 0 29

    Sporadic single failures through 10-11, then total failure on 10-14. OpenCode was 2.0.26 on both sides of the boundary and the plugin's last commit was 2026-10-09, before the successes were still landing — so this reads as the gateway's tool-set check tightening rather than a local change.

    The error string is identical to the reports above:

    Generate.UnavailableError: OpenCode's free tier can only be used from within OpenCode. Download it at https://opencode.ai and keep building.
    

    Why this case is worth separating out

    The documented workarounds in this thread all work by keeping a gated tool present in the list while denying its actions (bash: "*": deny plus a narrow allow, or ask instead of deny). None of them apply here, because there is no permission configuration to adjust — the caller supplies no tools at all. If the check is "does this body look like it came from OpenCode", then an explicitly tool-less generate.text from an in-process plugin is being judged by a heuristic that assumes tool presence implies OpenCode origin, and it will never satisfy it.

    Possible question for whoever picks this up: should generate.text from a plugin context be treated as trusted in-process, or should plugin callers be able to declare a tool list so the request still looks like an OpenCode request?

    Impact

    The affected plugin reviews every permission decision, so with its fail-open onError: "ask" default the session degrades to prompting on every tool call. Running the reviewer on a non-free-tier model avoids it entirely, but the plugin gives no way to choose a different reviewer model.

    Environment

    • opencode 2.0.26
    • Windows 11, NT 10.0.26200 x64, Windows Terminal, pwsh 7.6.7
    • install: latest
    • plugins: superpowers, @tarquinen/opencode-dcp, @mohak34/opencode-notifier, github:aSempruch/opencode-auto-mode
    • model at failure time: space-bunny-free on provider opencode
  13. hellovalerie commented on Oct 16, 2026

    @hellovalerie

    Confirming this from a downstream project that depends on restricting an agent's tools headlessly (automemory — a blind summarisation scribe that must not touch the filesystem). Full measured matrix on 1.19.0, macOS.

    Minimal trigger

    read must be removed from the tools list sent to the server. ask and allow keep it present and work; deny and false remove it and fail.

    Config selected via --agent? result
    permission: {read: "deny"} — 403
    permission: {read: "ask"} — OK
    permission: {read: "allow"} — OK
    permission: {} — OK
    permission: {bash: "deny"} — OK
    permission: {edit,glob,grep,list,bash,task,webfetch,websearch,lsp,skill: "deny"} (everything except read) — OK
    tools: {bash:false, write:false, edit:false} yes OK
    tools: {read:false, …} yes 403
    no permission block — OK

    --standalone makes no difference. Same account, same free tier, same model throughout.

    The workaround does not generalise to headless callers

    Agent-scoping the deny appears to work, but only when the agent isn't selected:

    Config --agent result
    agent.X.permission = {read: "deny"} no OK
    same agent, identical config yes 403 (3/3)

    opencode run --agent X makes X the session agent, so X's tools list is what reaches the server. For a headless scribe there is therefore no config-only way to deny read on the free tier. Worth stating plainly in #51241, since "scope it to a subagent" is the obvious next thing anyone will try.

    Two points that may narrow scope

    1. bash: "deny" does not reproduce (2/2 OK), while read denies reproducibly do (5/5 across both forms). [BUG] Free models fail when shell or read permissions are denied #51241 lists read or shell — I couldn't get a shell trigger through the bash permission key. Possibly a different key name, or version-specific.
    2. tools.read = false in opencode.json makes all zen free-tier models fail with "free tier can only be used from within OpenCode" #51315 (tools.read: false) is closed as v1, but this reproduces on 1.19.0 and per this thread affects v2 as well — so that closure may be premature.

    On the error text

    It says "OpenCode's free tier can only be used from within OpenCode. Download it at https://opencode.ai" — which is both the wrong diagnosis (not a tier gate) and self-contradictory advice (you are running OpenCode). It sends users to pay for behaviour that payment doesn't change. Flagging as a docs/message issue independent of the underlying bug.

    Filed originally as #55653.

    Co-Authored-By: opencode/space-bunny-free noreply@opencode.ai

  14. axisrow commented on Oct 16, 2026

    @axisrow

    Confirmed on OpenCode 1.19.2 (official npm opencode-ai@1.19.2), macOS 26.6.2 / arm64, using the built-in default agent and opencode/glm-5.3-flash-free in headless mode.

    Reproduction

    From the same empty working directory, start the server:

    OPENCODE_PERMISSION='{"*":"deny"}' opencode serve --hostname 127.0.0.1 --port 4096

    In another terminal, create a fresh session:

    opencode run --attach http://127.0.0.1:4096 \
      --model opencode/glm-5.3-flash-free --format json \
      'Ответь ровно HEADLESS_OK. Не вызывай инструменты и не изменяй файлы.'

    The prompt means: “Reply with exactly HEADLESS_OK. Do not call tools or change files.”

    Stop the server, restart it with only the permission value changed, then repeat the same client command in a new session:

    OPENCODE_PERMISSION='{"*":"ask"}' opencode serve --hostname 127.0.0.1 --port 4096

    Observed results

    Server permission Result
    {"*":"deny"} HTTP 403 FreeTierError: “OpenCode's free tier can only be used from within OpenCode. Download it at https://opencode.ai and keep building.”
    {"*":"ask"} HEADLESS_OK, finish: "stop"; no tools called

    Same installed version, model, credentials, configuration files, and proxy settings in both cases. The environment override was applied to the server process, which was restarted between cases.

    Results were verified through the stored GET /session/:id/message records. In this setup the attached CLI could exit with code 0 even for the rejected request, so exit code alone was insufficient.

    This reproduction uses a global tool denial; I did not isolate bash alone.

  15. neriousy commented on Oct 20, 2026

    @neriousy
    Member

    Thanks for the report. We're tracking this same free-tier rejection when shell access is denied in #49723, so I'm closing this as a duplicate. Please follow that issue for updates.

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