Background
While fixing a ConsecutiveMistakeError regression for DeepSeek V4 thinking models (PostHog issue 019e08dc), we found that Task.ts always passes tool_choice: "auto" when tools are present — allowing the model to return a plain text response instead of calling a tool.
For DeepSeek V4 thinking models this caused 24 production ConsecutiveMistakeError events (20 from deepseek-v4-pro, 4 from deepseek-v4-flash) where the model repeatedly returned text without calling any tool, hitting the 3-strike mistake limit.
Current fix
Scoped in PR #72: override "auto" → "required" inside DeepSeekHandler.createMessage when isThinkingModel is true and tools are present.
Holistic fix
Roo Code's architecture requires every turn to end with a tool call — attempt_completion is always included as a tool. tool_choice: "auto" is semantically incorrect here; "required" is the right value for all providers.
Proposed change: Update Task.ts to pass tool_choice: "required" wherever it currently passes "auto" (main task loop + condensing calls), and remove the DeepSeek-specific override.
Before doing this, verify:
- Anthropic already maps
"required" → "any" ✅
- OpenAI supports
"required" ✅
- OpenRouter passes it through ✅
- Gemini, Bedrock, Vertex, and other providers in the codebase handle
"required" correctly
- No provider silently ignores
"required" in a way that causes regressions
Why deferred
Higher blast radius than a provider-specific fix. If another provider exhibits similar no_tools_used flakiness in the meantime, add a targeted override (same pattern as the DeepSeek fix in PR #72) and note it here before the holistic fix lands.
Background
While fixing a
ConsecutiveMistakeErrorregression for DeepSeek V4 thinking models (PostHog issue 019e08dc), we found thatTask.tsalways passestool_choice: "auto"when tools are present — allowing the model to return a plain text response instead of calling a tool.For DeepSeek V4 thinking models this caused 24 production
ConsecutiveMistakeErrorevents (20 fromdeepseek-v4-pro, 4 fromdeepseek-v4-flash) where the model repeatedly returned text without calling any tool, hitting the 3-strike mistake limit.Current fix
Scoped in PR #72: override
"auto"→"required"insideDeepSeekHandler.createMessagewhenisThinkingModelis true and tools are present.Holistic fix
Roo Code's architecture requires every turn to end with a tool call —
attempt_completionis always included as a tool.tool_choice: "auto"is semantically incorrect here;"required"is the right value for all providers.Proposed change: Update
Task.tsto passtool_choice: "required"wherever it currently passes"auto"(main task loop + condensing calls), and remove the DeepSeek-specific override.Before doing this, verify:
"required"→"any"✅"required"✅"required"correctly"required"in a way that causes regressionsWhy deferred
Higher blast radius than a provider-specific fix. If another provider exhibits similar
no_tools_usedflakiness in the meantime, add a targeted override (same pattern as the DeepSeek fix in PR #72) and note it here before the holistic fix lands.