Skip to content

GH#17790: fix(opencode-plugin) strip trailing assistant messages to prevent Claude 4.x prefill error#17791

Open
superdav42 wants to merge 1 commit intomarcusquinn:mainfrom
superdav42:fix/opencode-prefill-guard
Open

GH#17790: fix(opencode-plugin) strip trailing assistant messages to prevent Claude 4.x prefill error#17791
superdav42 wants to merge 1 commit intomarcusquinn:mainfrom
superdav42:fix/opencode-prefill-guard

Conversation

@superdav42
Copy link
Copy Markdown
Contributor

@superdav42 superdav42 commented Apr 8, 2026

Summary

Adds a defensive plugin-level guard that strips trailing assistant messages from the outgoing LLM payload, preventing the "This model does not support assistant message prefill" error on Claude Opus/Sonnet 4.x — most often hit on the mobile webui.

Why

Claude Opus/Sonnet 4.x (Anthropic native, GitHub Copilot, OpenRouter, Bedrock, Vertex) reject any conversation that ends with an assistant message:

This model does not support assistant message prefill. The conversation must end with a user message.

opencode v1.3.17 / v1.4.0 has a partial gate at session/prompt.ts (lastAssistant2?.finish && !["tool-calls"].includes(...)) that handles the most common continuation case, but it fails open when finish is undefined — which is exactly the state left by aborted/interrupted previous turns. Mobile users hit this constantly because flaky connections cause aborts.

Three upstream PRs (sst/opencode#14772, #16921, #18091) all fix this in provider/transform.ts by adding stripTrailingAssistant(). None are merged.

What

  • NEW .agents/plugins/opencode-aidevops/prefill-guard.mjs — pure module exporting createPrefillGuardHook(deps). Strips trailing assistant messages from output.messages only when safe:
    • Preserves messages with finish === "tool-calls"
    • Preserves messages with active tool parts (pending / running / completed)
    • Strips assistants whose tool parts are all in error / aborted state, or that have no tool parts
  • EDIT .agents/plugins/opencode-aidevops/index.mjs — imports the guard and composes it into the experimental.chat.messages.transform hook after the existing messagesTransformHook so TTSR's rule scanning still sees all assistants before they're stripped.

Safety properties

  • Session DB is never modified — stripped messages still display in transcript history. Only the outgoing LLM payload is altered.
  • Tool-call flows preserved — assistants with live tool calls are never stripped. Only assistants whose tool parts are all in terminal error/aborted states (or that have no tool parts at all) are eligible.
  • Never throws — wrapped in try/catch with diagnostic logging via qualityLog. A bug here cannot break the hook chain.
  • Provider-agnostic — works for Anthropic native, GitHub Copilot, OpenRouter, Bedrock, Vertex, and any other path that hits this hook.
  • Composition order matters: TTSR runs first because it scans the last 3 assistants for rule violations and may append a synthetic user correction (which itself fixes the user-final invariant). The guard runs second so anything TTSR didn't append a correction for still gets stripped.

Verification

# Syntax check both files
node --check .agents/plugins/opencode-aidevops/prefill-guard.mjs
node --check .agents/plugins/opencode-aidevops/index.mjs

# Restart and verify clean plugin load
systemctl --user restart opencode-web.service
journalctl --user -u opencode-web.service --since "now" | grep -iE "aidevops|fail|exception"

# Confirm guard fires when stripping (only on actual triggering message)
journalctl --user -u opencode-web.service -f | grep prefill-guard

The fix has been deployed and verified locally — opencode-web restarted cleanly, plugin loaded without errors, and the mobile webui has been working since.

Related upstream

This plugin-level guard is a workaround until one of those upstream PRs lands. When they do, this guard becomes a no-op (the upstream strip will happen first) and can optionally be removed.

Resolves #17790


aidevops.sh v3.6.162 plugin for OpenCode v1.3.17 with claude-opus-4-6 spent 1h 12m and 52,593 tokens on this with the user in an interactive session.

Summary by CodeRabbit

  • New Features
    • Added automatic filtering mechanism for assistant messages during conversation processing to maintain chat quality

…aude 4.x prefill error (GH#17790)

Adds prefill-guard.mjs to opencode-aidevops plugin and composes it into the
experimental.chat.messages.transform hook after the existing TTSR hook.

The guard strips trailing assistant messages from the outgoing LLM payload
when safe — preserving messages with finish=tool-calls or active tool parts
so legitimate tool-call flows are untouched. Session DB is never modified.

Fixes the mobile webui error 'This model does not support assistant message
prefill' on Claude Opus/Sonnet 4.x. Mirrors upstream PRs anomalyco/opencode#14772,
marcusquinn#16921, and #18091 which add the same logic in provider/transform.ts but are
not yet merged in opencode v1.3.17 / v1.4.0.

Fixes marcusquinn#17790
@gemini-code-assist
Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

Maintainer Gate: BLOCKED

This PR cannot be merged because:

Issue #17790 has needs-maintainer-review label — a maintainer must review and approve the issue before it can be worked on.
Issue #17790 has no assignee — an issue must be assigned before work begins.
What to do:

  1. A maintainer must review the linked issue(s) and approve them using the cryptographic approval command:
    sudo aidevops approve issue <issue_number>
    
    This requires your system password and signs the approval with a root-protected key that automation cannot forge.
  2. The issue(s) must have an assignee before work begins
  3. This check re-runs automatically when the issue is updated

This is an automated check. See issue-triage-gate.yml for the triage policy.

@github-actions github-actions bot added the bug Auto-created from TODO.md tag label Apr 8, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

Walkthrough

This adds a defensive prefill guard to the opencode-aidevops plugin that strips trailing assistant messages from chat conversations before they reach Claude 4.x models, preventing Anthropic's prefill validation errors while preserving legitimate tool-call flows.

Changes

Cohort / File(s) Summary
Hook Composition
.agents/plugins/opencode-aidevops/index.mjs
Imports createPrefillGuardHook and integrates it into the experimental.chat.messages.transform pipeline by composing it with the existing messagesTransformHook. TTSR runs first to scan and fix rule violations, then the guard strips any remaining trailing assistant messages that could trigger prefill errors.
Prefill Guard Module
.agents/plugins/opencode-aidevops/prefill-guard.mjs
New module exporting createPrefillGuardHook factory. Implements a defensive hook that walks backward through trailing assistant messages, safely removing only those without active tool calls (finish !== "tool-calls" and no pending/running/completed tool parts). Includes try/catch wrapping and diagnostic logging per stripped message (provider, model, finish state, part count).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🛡️ A guard stands watch at the message gate,
Strips trailing assistants before it's too late,
Tool-calls untouched, the logic runs clean,
No prefill errors on Claude 4.x scene! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: adding a prefill guard to strip trailing assistant messages that prevent Claude 4.x from processing requests.
Linked Issues check ✅ Passed The PR implements all coding requirements from issue #17790: strips trailing assistant messages safely, composes hooks in correct order, preserves tool-call flows, wraps in try/catch, and provides diagnostic logging.
Out of Scope Changes check ✅ Passed All changes are scoped to the prefill-guard implementation and its composition into the opencode-aidevops plugin; no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production
Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 2 medium

Results:
2 new issues

Category Results
Complexity 2 medium

View in Codacy

🟢 Metrics 33 complexity

Metric Results
Complexity 33

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

alex-solovyev added a commit that referenced this pull request Apr 8, 2026
Add _check_linked_issue_gate() to full-loop-helper.sh cmd_start that
mirrors the CI maintainer-gate.yml check-pr job logic. Workers now fail
fast locally before creating PRs that will always fail the CI gate.

- Extracts issue number from prompt (#NNN pattern)
- Checks needs-maintainer-review label → blocks with clear message
- Checks no assignee (exempt: quality-debt issues per GH#6623) → blocks
- Skips gracefully for closed issues, missing issue numbers, API errors
- Documents the check in full-loop.md as a mandatory pre-start step

Root cause: PR #17791 was created for issue #17790 (CONTRIBUTOR-authored,
needs-maintainer-review applied by triage gate) before the issue was
approved. The CI gate correctly blocked the PR. This fix prevents workers
from starting work on unapproved external issues.

Fixes #17810
marcusquinn pushed a commit that referenced this pull request Apr 8, 2026
#17812)

Add _check_linked_issue_gate() to full-loop-helper.sh cmd_start that
mirrors the CI maintainer-gate.yml check-pr job logic. Workers now fail
fast locally before creating PRs that will always fail the CI gate.

- Extracts issue number from prompt (#NNN pattern)
- Checks needs-maintainer-review label → blocks with clear message
- Checks no assignee (exempt: quality-debt issues per GH#6623) → blocks
- Skips gracefully for closed issues, missing issue numbers, API errors
- Documents the check in full-loop.md as a mandatory pre-start step

Root cause: PR #17791 was created for issue #17790 (CONTRIBUTOR-authored,
needs-maintainer-review applied by triage gate) before the issue was
approved. The CI gate correctly blocked the PR. This fix prevents workers
from starting work on unapproved external issues.

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

Labels

bug Auto-created from TODO.md tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(opencode-plugin): strip trailing assistant messages to prevent Claude 4.x prefill error

2 participants