Skip to content

llm: stray closing think marker persisted as visible assistant text (opencode/big-pickle) #56104

Closed as duplicate

Description

@glucmastersten-cpu

Summary

With the built-in opencode/big-pickle model, reasoning is intermittently persisted as an
ordinary text part instead of a reasoning part, and the literal closing thinking marker
</think> leaks into the visible assistant text. In 2.0.29/2.0.30 the marker appeared at
the start of the reply (</think>Готово…); 2.0.31 reduces that shape roughly 16x, but a
rarer variant remains where the whole thinking trace is stored as text ending in the marker.

Environment

  • opencode version: 2.0.31 (the issue also reproduced on 2.0.29 and 2.0.30)
  • OS: Windows 10 19045.6456 (10.0.19045)
  • Terminal: VS Code integrated terminal (TERM_PROGRAM=vscode, TERM=xterm-256color, COLORTERM=truecolor)
  • Shell: cmd.exe (C:\Windows\system32\cmd.exe)
  • Install/channel: npm global @opencode/cli, prefix D:\Programs\0.IA\npm-global
  • Active plugins: none found in config

Reproduction

  1. Select model opencode/big-pickle.
  2. Send a prompt that makes the model think and then call a tool, e.g.
    вызови инструмент glob 12 раз последовательно, по одному вызову за шаг.
  3. Inspect the stored assistant parts (session_message in opencode.db) or watch the
    rendered reply.

The marker sometimes shows up as a separate leading text part and sometimes as part of the
assistant's final message, so the visible symptom is a stray </think> before the answer.

Expected Behavior

Reasoning should be stored in a reasoning part, and the thinking / </think> markers
should never appear in visible assistant text nor be replayed to the model as assistant
content.

Actual Behavior

A text part starts with, or contains, the literal marker </think>, and because it is
persisted as an ordinary text part it is also replayed into the next request as assistant
content.

Raw stored examples from 2.0.29 / 2.0.30 (marker at the start, immediately before the answer):

"</think>Готово.\n\n- `churnCount`: ..."
" responseГотово, можно проверять.\n\n- ..."
" responseГотово.\n\n- ..."

In that session 32 assistant messages were affected. Across the whole database: 191 of
12059 assistant messages, always with the same model (opencode/big-pickle), never with
other providers configured on the machine.

Additional Context

Measured on one machine with the same model, reading the session DB and counting assistant
messages that contain a tool call:

opencode assistant msgs with tool call msgs whose text part starts with </think>
2.0.29 661 105 (15.9%)
2.0.30 165 29 (17.6%)
2.0.31 88 1 (1.1%)

So 2.0.31 largely removes the highly visible "marker before the answer" shape, but the
underlying "reasoning stored as text" problem is not fully gone. Residual 2.0.31 examples
(raw stored text parts):

"The glob tool isn't in the Code Mode catalog - only opencode tools are. ..."
"Try search({query:...}) or search({pattern:...}).</think>"
"</think>"   (lone text part immediately before a tool call)

The leak is frequency-based, not deterministic: the same model and the same prompt produce
it only in a subset of tool-call turns (~18% before the upgrade, ~1% after).

Related: #34126 (standalone </think> before tool_calls persisted as assistant text) and
its fix PR #34698, which was closed without being merged; #43770 (reasoning inlined in
content leaks into text); #11439.

Note: in the v2.0.31 binary the literal marker strings are not present, while
reasoning_content / reasoningField / reasoningEnd are. Worth verifying that the
reasoning-to-tool boundary suppression actually ships in v2 builds (the v1 fix lived in
packages/llm/src/protocols/openai-chat.ts).

Workarounds tried

Upgrading 2.0.29 -> 2.0.31 reduces the visible marker-before-the-answer shape from ~16% to
~1% of tool-call messages, but does not eliminate the leak entirely. No configuration
workaround found.

Activity

  1. opencode-agent commented on Oct 16, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report and the per-version measurements, @glucmastersten-cpu.

    I think this is a duplicate of #54599. In both, opencode/big-pickle sends its reasoning inline in the reply, ending in a literal </think> with no opening tag, and OpenCode saves it as a normal text part instead of a reasoning part. That issue has already been reproduced on v2, and #54612 proposes a fix. The fix splits the inline reasoning out where the stream is parsed (packages/ai/src/protocols/openai-chat.ts), turned on for opencode/big-pickle, and has not been merged yet. That fits what you saw: there are no marker strings in the 2.0.31 binary.

    Possible duplicates and related issues, ordered by likelihood:

    Your numbers for 2.0.29 → 2.0.31 and the remaining 2.0.31 examples would be useful as a comment on #54599.

    The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different.

  2. opencode-agent commented on Oct 17, 2026

    @opencode-agent
    Contributor

    Closing as a duplicate of #54599, since there was no reply saying how this differs. Both are about opencode/big-pickle sending its reasoning inline with a literal </think>, which OpenCode then saves as visible text. Please follow #54599 and the proposed fix in #54612. Your numbers for 2.0.29 → 2.0.31 and the remaining 2.0.31 examples would help there, so please add them as a comment on #54599. If your problem turns out to be different, reply here and we can reopen. Thanks, @glucmastersten-cpu!

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