Skip to content

fix(core): continue after an empty model response - #61530

Open
kitlangton wants to merge 2 commits into
devfrom
gemini-empty-stop
Open

kitlangton wants to merge 2 commits into
devfrom
gemini-empty-stop

Conversation

@kitlangton

@kitlangton kitlangton commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #55378

Type of change

  • Bug fix

What does this PR do?

An empty successful model reply gets one chance to continue instead of silently stopping the session. Seen with a Gemini model after compaction, but the recovery applies to every provider.

❌ Before

  1. The model returns a successful stop with no text or tool calls.
  2. OpenCode goes idle even though the task is unfinished.

✅ After

  1. OpenCode adds a short continuation prompt and reloads the conversation.
  2. If the next response is also empty, it reports an error. Reasoning-only and whitespace-only responses count as empty. Text or tool output resets the allowance, including during output-limit or interrupted-stream recovery.
- successful stop -> session goes idle
+ empty successful stop -> one continuation prompt
+ another empty response -> visible error

👀 Read: runner/step.ts checks for an empty response only after cancellation and errors have been handled. runner/llm.ts bounds recovery without resetting the agent's step allowance.

How did you verify your code works?

Case Before After
Empty reply after compaction ❌ Stops ✅ Continues
Repeated empty replies ❌ Silent stop ✅ Error after one retry

The compaction regression failed before the fix. Existing completion fixtures now return a short answer instead of an empty stop.

Checks run

From packages/core:

  • bun run test test/session-runner.test.ts test/session-runner-tool-events.test.ts test/session-runner-recorded.test.ts: 284 pass, including cancellation, errors, and mixed-recovery coverage.
  • bun typecheck: passed.

From the repository root:

  • bun run check: passed, including the final pre-push hook; existing lint warnings, no errors; all 35 typecheck tasks passed.

Visual

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="72" viewBox="0 0 240 72">
  <style>@keyframes pulse{0%,100%{opacity:.35}50%{opacity:1}}.retry{animation:pulse 2s ease-in-out infinite}@media(prefers-reduced-motion:reduce){.retry{animation:none}}</style>
  <g fill="none" stroke="currentColor" stroke-width="1.5">
    <rect x="10" y="16" width="54" height="40" rx="6" opacity=".4"/>
    <path class="retry" d="M76 36h34m-7-6 7 6-7 6"/>
    <rect x="122" y="16" width="106" height="40" rx="6"/>
    <path d="M135 29h65m-65 7h78m-78 7h49" opacity=".65"/>
  </g>
</svg>

Checklist

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

Added by the opencode bot (AI-generated).

Fixes #59518
Fixes #46880

Related: #60292, #59051, #52015

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change works. On the base, a reasoning-only response that finishes with stop leaves the session idle with nothing but "Thought". With this PR, OpenCode sends one continuation request and shows the follow-up answer. If the model keeps returning nothing, the TUI shows "The model returned no answer or tool calls after one retry" after a single retry. I checked this by driving the TUI with the simulated model on both checkouts. The three runner test files pass (284), and tsc -b for packages/core is clean.

The public API is unchanged: the new Empty step outcome and the empty record field are internal to the runner.

One thing to be aware of, not blocking: on strict OpenAI-compatible providers (e.g. Kimi), the retry replays the reasoning-only assistant turn with empty content. That can get a 400 instead of continuing, the same failure as #60292. It is still a visible error rather than a silent stop.

Fixes #59518
Fixes #46880
Related: #60292, #59051, #52015

Before

After

This branch has not been deployed

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

Projects

None yet

1 participant