Skip to content

fix(session): clone tool input before passing to EventV2 to prevent Immer freeze#25867

Open
stephanschielke wants to merge 2 commits intoanomalyco:devfrom
stephanschielke:fix/git-runfold-readonly-mutation
Open

fix(session): clone tool input before passing to EventV2 to prevent Immer freeze#25867
stephanschielke wants to merge 2 commits intoanomalyco:devfrom
stephanschielke:fix/git-runfold-readonly-mutation

Conversation

@stephanschielke
Copy link
Copy Markdown

@stephanschielke stephanschielke commented May 5, 2026

Issue for this PR

Closes #25873

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Fixes TypeError: Attempted to assign to readonly property that occurs on every tool call when OPENCODE_EXPERIMENTAL=true and a plugin mutates tool args in tool.execute.before hooks (e.g. oh-my-openagent).

Root cause: In processor.ts:329, the tool-call event handler passes value.input by reference to EventV2.run(). This reference is the same object as the tool's args in execute(args, options). The call flows through SyncEvent.run -> SessionMessageUpdater.update() -> Immer produce(), which calls Object.freeze() on the entire produced state tree, including the original args object. When a plugin hook subsequently tries to mutate args.command, it throws.

Fix: Clone the input before passing to EventV2.run so Immer freezes the clone instead of the original:

-              input: value.input,
+              input: structuredClone(value.input),

Instrumented proof:

BEFORE EventV2.run: value.input frozen=false
AFTER EventV2.run:  value.input frozen=true

How did you verify your code works?

  1. Built v1.14.34 binary with OPENCODE_EXPERIMENTAL=true + oh-my-openagent plugin
  2. Unpatched: 8/8 tool calls crash with Attempted to assign to readonly property
  3. Patched: 8/8 tool calls succeed
  4. OPENCODE_EXPERIMENTAL=false: 8/8 succeed (confirms the flag gates the code path)
  5. git bisect across 12,213 commits identified commit a3bc5d35b (PR Refactor v2 session events as schemas #24512) as the first bad commit
  6. File-level revert of only processor.ts fixes the crash
  7. Stubbing EventV2.run as a no-op fixes the crash

Screenshots / recordings

N/A (not a UI change)

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. and removed needs:compliance This means the issue will auto-close after 2 hours. needs:issue labels May 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@stephanschielke stephanschielke changed the title fix(git): replace mutating Stream.runFold with Stream.runForEach fix: mutable Stream.runFold crash in compiled binaries, revert effect to beta.57 May 6, 2026
@stephanschielke stephanschielke changed the title fix: mutable Stream.runFold crash in compiled binaries, revert effect to beta.57 fix: replace all mutable Stream fold/collect patterns with immutable runForEach, revert effect to beta.57 May 6, 2026
@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. and removed needs:compliance This means the issue will auto-close after 2 hours. labels May 6, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

Thanks for updating your PR! It now meets our contributing guidelines. 👍

…mmer freeze

The v2 event system passes value.input by reference to EventV2.run(),
which routes it through SyncEvent -> SessionMessageUpdater -> Immer
produce(). Immer freezes the entire produced state tree including
value.input, which is the same object reference as the tool args.

When a plugin (oh-my-openagent) later tries to mutate args in a
tool.execute.before hook, the frozen object throws:
  TypeError: Attempted to assign to readonly property.

Clone the input before passing to EventV2.run so Immer freezes the
clone instead of the original args object.

Conditions: OPENCODE_EXPERIMENTAL=true + plugin that mutates tool args.

Fixes anomalyco#25873
@stephanschielke stephanschielke force-pushed the fix/git-runfold-readonly-mutation branch from ba76c56 to 00d8629 Compare May 7, 2026 23:07
@stephanschielke stephanschielke changed the title fix: replace all mutable Stream fold/collect patterns with immutable runForEach, revert effect to beta.57 fix(session): clone tool input before passing to EventV2 to prevent Immer freeze May 7, 2026
@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label May 7, 2026
@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label May 7, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Thanks for updating your PR! It now meets our contributing guidelines. 👍

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bash tool fails with 'Attempted to assign to readonly property' in v1.14.34

1 participant