deepseek-ai / deepseek-ai/DeepSeek-V3

[Bug] DeepSeek-V4-Pro intermittently emits tool calls as plain text in content instead of tool_calls field

Open
#1,244 26 comments 5 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
104k
Forks
16.7k
PR merge metrics
No merged PRs in 30d

Description

Description

When using DeepSeek-V4-Pro via the OpenAI-compatible chat completions API with tools enabled, the model
intermittently outputs function-call invocations as raw text inside the content field, rather than generating a
proper tool_calls response object. The finish_reason is stop (instead of tool_calls), and the tool_calls field is
null.

This happens non-deterministically — in the same multi-turn conversation, most tool calls are emitted correctly, but
occasionally the model "falls through" to text mode and writes the function name + JSON arguments verbatim into the
content.

Reproduction rate

In a single multi-turn session (19 completions with tools enabled):
- 15/19 (79%) — Correct: finish_reason: "tool_calls", proper tool_calls array
- 2/19 (11%) — Bug: finish_reason: "stop", tool_calls: null, function call syntax appears in content
- 2/19 (10%) — Normal text reply (no tool call intended), finish_reason: "stop" — expected behavior

Request setup

{
"model": "deepseek-v4-pro",
"stream": true,
"messages": [
{"role": "system", "content": "..."},
{"role": "user", "content": "..."},
{"role": "assistant", "content": null, "tool_calls": [{"id": "call_xxx", "type": "function", "function":
{"name": "batch_web_search", "arguments": "{...}"}}]},
{"role": "tool", "tool_call_id": "call_xxx", "content": "..."},
{"role": "user", "content": "..."}
// ... multi-turn conversation with prior successful tool calls
],
"tools": [
{
"type": "function",
"function": {
"name": "web_search",
"description": "Search the web for information.",
"parameters": { "type": "object", "properties": { "queries": { "type": "array", "items": { "type": "string"
} } }, "required": ["queries"] }
}
},
{
"type": "function",
"function": {
"name": "batch_crawl_url_and_answer",
"description": "Retrieves web content from multiple URLs in parallel and extracts key information.",
"parameters": { "type": "object", "properties": { "jobs": { "type": "array", "items": { "type": "object",
"properties": { "url": { "type": "string" }, "questions_to_answer": { "type": "array", "items": { "type": "string" }
} }, "required": ["url", "questions_to_answer"] } } }, "required": ["jobs"] }
}
}
// ... ~40 tools total
]
}

Expected response

{
"finish_reason": "tool_calls",
"content": null,
"tool_calls": [
{
"id": "call_...",
"type": "function",
"function": {
"name": "batch_crawl_url_and_answer",
"arguments": "{\"jobs\": [{\"url\": \"https://example.com\", \"questions_to_answer\": [\"...\"]}]}"
}
}
]
}

Actual (buggy) response

{
"finish_reason": "stop",
"content": "数据还不够完整,让我继续获取更详细的指标。需要抓取专业 benchmark
对比网站获取全面数据。\nbatch_crawl_url_and_answer{\"jobs\": [{\"url\":
\"https://www.example.com/model-benchmarks\", \"questions_to_answer\": [\"All benchmark scores: MMLU, HumanEval,
MATH, GPQA, SWE-bench, etc.\"]}, {\"url\": \"https://www.example.com/leaderboard\", \"questions_to_answer\":
[\"Latest benchmark scores for major models\"]}]}",
"tool_calls": null
}

The model writes natural language first, then appends the tool name and raw JSON arguments directly into the content
— as if it is "thinking aloud" and then typing the function call instead of triggering the tool-call code path.

Observed pattern


Image

The failures are not consecutive — they appear randomly interspersed with correct tool calls.

Additional observations

1. Both buggy responses begin with Chinese text before the tool call. The model appears to emit a brief "thinking"
sentence first, then appends the tool call as text. In correct cases, the model either emits tool_calls directly or
emits a short content string separately.
2. Streaming usage fields are all zeros in the buggy responses, suggesting the streaming chunks may have been
malformed or the response was not properly terminated.
3. Prior successful tool calls in the same conversation rule out configuration errors — the same tools array works
correctly 79% of the time.
4. Tested via OpenAI-compatible /v1/chat/completions endpoint, streaming mode.

Environment

- API endpoint: DeepSeek official API (OpenAI-compatible)
- Model: deepseek-v4-pro (also labeled deepseek_v4_pro_test in some responses)
- Streaming: enabled
- Tools: ~40 function definitions
- Date: 2026-04-24

Impact

This causes downstream tool execution to silently fail — the orchestrating system receives a plain stop response
with text content and has no structured tool call to dispatch. The user sees the tool call parameters echoed as raw
text in the chat instead of the tool being executed.

Suggested fix

The model's function-calling alignment should ensure that when the intent is to invoke a tool, the response always
uses the structured tool_calls format, regardless of whether the model prepends natural language text. If the model
wants to emit text and a tool call in the same turn, it should use content + tool_calls together (as the OpenAI spec
allows), not serialize the tool call into the content string.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.