Skip to content

Intermittent pseudo [tool_call: ...] narration emitted as text before real tool event #18074

Closed as not planned

Description

@liujuanjuan1984

Description

opencode serve intermittently emits a plain text narration that looks like a tool call before it emits the real structured tool event.

In the affected turns, the stream first contains text like:

I'll gather the requested information about the working directory and git branches.

[tool_call: bash for 'pwd && git branch' because I need to identify the absolute path and list all local branches]

Then, shortly afterwards, opencode serve emits the actual structured tool part for the same bash command.

This is not deterministic. The same workflow sometimes behaves correctly and emits the real tool event without the extra pseudo [tool_call: ...] narration.

Environment

  • provider/model: google/gemini-3.1-pro-preview
  • launched with opencode serve

Why this looks like an opencode serve issue

We verified the downstream consumer logs.

For the affected turn, the downstream bridge received two different upstream events:

  1. a text part containing the pseudo tool-call narration
  2. a later tool part for the real bash invocation

The downstream bridge classified them correctly:

  • the narration stayed text
  • the real tool invocation became tool_call

So this does not look like a downstream rendering/classification bug. It looks like opencode serve itself is sometimes streaming an extra natural-language tool narration before the real structured tool event.

Actual Behavior

For one turn, the stream looks like this:

  1. text narration about gathering the requested information
  2. plain text pseudo marker: [tool_call: bash for 'pwd && git branch' because ...]
  3. real structured bash tool event
  4. final normal text answer

This creates duplicated and confusing output for clients, because users see both:

  • a fake textual tool-call marker
  • the real tool call event

Expected Behavior

If a tool call is going to happen, opencode serve should emit only the structured tool event, not a pseudo [tool_call: ...] marker inside normal text.

Minimal Reproduction Pattern

This happens intermittently when asking for information that requires a bash tool call, for example:

  • current working directory
  • current git branch
  • local branch list

A representative command is:

pwd && git branch

Evidence

From downstream logs, the affected turn clearly shows:

  • first: message.part.delta / message.part.updated for a text part containing the pseudo [tool_call: ...] narration
  • later: a separate message.part.updated with part.type=tool for the actual bash execution

So the extra narration is already present in the opencode serve stream output.

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Mar 18, 2026
  2. github-actions commented on Mar 18, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of an existing issue. Please check:

    • fix(provider): Copilot Gemini models fail to produce structured tool calls #15315: Copilot Gemini models fail to produce structured tool calls — describes the same symptom where Gemini models emit plain-text [tool_call: ...] narration instead of structured tool call events. The root cause identified there is that sanitizeGemini schema transforms are incorrectly applied to Copilot-proxied Gemini models, but a similar schema transform issue may also affect google/gemini-3.1-pro-preview via opencode serve.

    If your issue is distinct (e.g. it occurs with the native Google provider rather than GitHub Copilot, or you've confirmed the schema transforms are not involved), please clarify and this note can be removed.

  3. liujuanjuan1984 commented on Mar 18, 2026

    @liujuanjuan1984
    Author

    Thanks. This looks related to #15315, but based on our logs it does not appear to be the same failure mode.

    What we observed on our side:

    • provider: google
    • reproduced model in the captured logs: gemini-3-flash-preview
    • this is intermittent, not deterministic

    The important difference is that in our case OpenCode does eventually emit the real structured tool part.

    From the captured stream:

    1. OpenCode first emits a text part containing a pseudo narration like:
      I'll gather the requested information about the working directory and git branches.
      
      [tool_call: bash for 'pwd && git branch' because I need to identify the absolute path and list all local branches]
      
    2. Later in the same turn, OpenCode emits a real tool part for the bash call.

    So the behavior we are seeing is:

    • extra plain-text pseudo tool-call narration first
    • real structured tool event afterwards

    That is why we suspect this issue is related to Gemini tool-calling / transform logic, but not necessarily a duplicate of #15315, which describes a case where structured tool calls are not produced.

    If helpful, we can provide a sanitized event sequence showing:

    • message.part.updated with part.type=text
    • later message.part.updated with part.type=tool

    So at this point the strongest statement we can make is:

  4. liujuanjuan1984 commented on Mar 18, 2026

    @liujuanjuan1984
    Author

    One clarification in case it helps narrow the symptom:

    In the affected turns, we are not only seeing text that contains a pseudo [tool_call: ...] marker.
    We also do see a real structured tool call afterwards in the same turn.

    So the observed sequence is specifically:

    1. a text part whose content includes [tool_call: ...]
    2. then an actual structured tool part is emitted and the tool call really happens

    So this is not just "tool call collapsed into text".
    It is "textual pseudo tool-call narration appears first, while the real tool call still happens afterwards".

  5. removed
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  6. github-actions commented on Jul 3, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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