Skip to content

plan mode: write/edit into the configured plan directory denied as "outside the Plan directory" #44694

Closed as not planned

Description

@jc0d35

Summary

In Plan mode, both the write and edit tools refuse to touch the configured plan directory. The error names /home/cunabu/.opencode/plan as THE plan directory, then rejects /home/cunabu/.opencode/plan/ai-benchmark-overhaul.md as being outside it. The same failure hits brand-new files and pre-existing files in that directory, so the target path is being compared against the wrong allowlist entry. Suspects: an inverted condition (allowing only paths that do not start with the plan dir), or an unnormalized path against a normalized entry (realpath / trailing slash).

The mode's contract is exactly "you may create or update plan documents in this directory"; the guard blocks precisely that.

Environment

  • opencode version: opencode2 v0.0.0-beta-18050 (beta)
  • OS: Linux 6.8.0-111-generic x86_64
  • Terminal: tmux (TERM=tmux-256color)
  • Shell: /bin/bash
  • Install/channel: beta
  • Active plugins: /home/cunabu/.config/opencode/plugins/cunabu-agent-git.mjs

Reproduction

  1. Start a Plan-mode session whose plan directory is /home/cunabu/.opencode/plan.
  2. Invoke write on an existing file inside it, e.g. /home/cunabu/.opencode/plan/ai-benchmark-overhaul.md.
  3. Observe the denial. Repeat with edit. Repeat with a brand-new filename in the same directory.

Expected Behavior

The write/edit succeeds; the mode contract explicitly grants plan-directory writes.

Actual Behavior

Cannot use write to modify files outside the Plan directory: /home/cunabu/.opencode/plan

(edit produces the identical message with its own tool name.)

Additional Context

  • Path is canonical: readlink -f is identity (no symlink), owned by the session user (uid 1000), dir mode 700, file mode 600. Not ownership/ACL.
  • opencode.json and cli.json under ~/.config/opencode/ contain no plan-directory setting, so the message's own value is the only allowlist source.
  • Workaround used: shell redirection into that directory (succeeds) — which routes file edits around the tool layer and loses tool-level auditing, the exact behavior a path guard should prevent.
  • Suggested fix + test: canonicalize both sides and allow path.startsWith(planDir + sep); add a regression test covering both an existing and a brand-new file inside the plan dir.

Activity

  1. opencode-agent commented on Aug 24, 2026

    @opencode-agent
    Contributor

    Thanks for the report. We tested the exact v0.0.0-beta-18050 build with isolated configuration and real Plan-mode tool calls. Both creating a new file with write and modifying an existing file with edit under ~/.opencode/plan/ succeeded, so we’re unable to reproduce this.

    If this happens again, please include a shared session transcript so we can inspect the exact tool call and determine whether this is a prompting/tool-selection problem or a permission failure.

  2. luke9705 commented on Aug 31, 2026

    @luke9705

    This still reproduces on v0.0.0-beta-18721 (this was closed after testing beta-18050), and on a different platform — macOS 26.6.2 arm64, vs the original Linux x86_64 report. Filed as #46358 with full detail.

    Adding one test that wasn't in the original report and that should address the "prompting/tool-selection problem" possibility raised when this was closed:

    In a single Plan-mode session, with two paths that both have matching allow rules in the agent's resolved permissions:

    Target Result
    ~/.opencode/plan/003-example.md Cannot use write to modify files outside the Plan directory: /Users/<user>/.opencode/plan
    ~/aidocs/guard-probe.md succeeded

    The same tool, in the same session, writes successfully to one path and is refused for the other. So the tool call is being issued correctly — this is not tool selection or prompting — and it is not a blanket Plan-mode write block. The guard discriminates by path and does not recognise ~/.opencode/plan as the plan directory it names in its own error message.

    The path that succeeds is project-relative; the one that fails is absolute. That is consistent with the unnormalized/wrongly-based comparison hypothesis in the original report.

    One environmental detail that may explain why this didn't reproduce on your side: the session's working directory here is the user's home directory (/Users/<user>), which is also the detected project root, and the plan directory ~/.opencode/plan therefore sits underneath it. If the guard derives the plan directory from the project/session location rather than the advertised absolute path, a home-directory session seems a plausible trigger. Might be worth testing with the session rooted at $HOME.

    Also relevant: ~/.opencode here contains memory/, node_modules/, package.json, and .gitignore alongside plan/, and the plan directory has pre-existing files in it.

    I don't have a shareable session transcript, which is what was requested — but the contrast above should serve the same diagnostic purpose.

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions