zeroclaw-labs/zeroclaw · error · SemanticEmptyTerminalCompletion
provider completed without final text or tool calls
Error message
provider completed without final text or tool calls
What it means
Default ModelProvider::chat implementation for providers without native tool calling: tool schemas are folded into the system prompt, a plain string completion is requested, and the wrapped ChatResponse must survive is_semantically_empty_terminal(). If the model returns nothing but whitespace or <think> reasoning, the typed SemanticEmptyTerminalCompletion error is raised so reliable/SOP delivery layers can distinguish 'model produced no usable output' from success.
Source
Thrown at crates/zeroclaw-api/src/model_provider.rs:681
system_message.content.push_str("\n\n");
}
system_message.content.push_str(&tool_instructions);
} else {
modified_messages.insert(0, ChatMessage::system(tool_instructions));
}
let text = self
.chat_with_history(&modified_messages, model, temperature)
.await?;
let response = ChatResponse {
text: Some(text),
tool_calls: Vec::new(),
usage: None,
reasoning_content: None,
};
return (!response.is_semantically_empty_terminal())
.then_some(response)
.ok_or_else(|| anyhow::Error::new(SemanticEmptyTerminalCompletion));
}
let text = self
.chat_with_history(request.messages, model, temperature)
.await?;
let response = ChatResponse {
text: Some(text),
tool_calls: Vec::new(),
usage: None,
reasoning_content: None,
};
(!response.is_semantically_empty_terminal())
.then_some(response)
.ok_or_else(|| anyhow::Error::new(SemanticEmptyTerminalCompletion))
}
/// Whether model_provider supports native tool calls over API.
fn supports_native_tools(&self) -> bool {View on GitHub (pinned to 88bb9c8533)
Solutions
- Inspect the raw response of the failing model directly; if it is think-only, raise max_tokens or switch to a non-reasoning model for tool-using turns.
- Use a provider/endpoint that supports native tool calling so tool-call-only turns are valid instead of falling into the prompt-guided string path.
- Put the model behind the reliable provider chain so a semantic-empty on one model falls through to the next candidate.
- Check the provider capabilities configuration; a wrong capabilities flag can route a native-tools provider into this degraded path.
Example fix
// before: reasoning model on the prompt-guided string path emits only <think>...</think>
let resp = provider.chat(ChatRequest { messages, tools: Some(&tools), .. }, model, None).await?;
// after: chain a non-reasoning fallback via the reliable provider
let resp = reliable.chat(request, None).await // falls through on SemanticEmptyTerminalCompletion
.or_else(|e| if e.is::<SemanticEmptyTerminalCompletion>() { retry_with_plain_model() } else { Err(e) })?; Defensive patterns
Strategy: fallback
Validate before calling
fn is_semantically_empty(text: &str) -> bool {
zeroclaw_api::model_provider::strip_think_tags(text).trim().is_empty()
} Try / catch
match provider.chat(request, model, temperature).await {
Ok(resp) => Ok(resp),
Err(e) if e.is::<zeroclaw_api::model_provider::SemanticEmptyTerminalCompletion>() => {
fallback_provider().chat(request, fallback_model, temperature).await // typed terminal -> next candidate
}
Err(e) => Err(e),
} Prevention
- Prefer providers with native tool calling for tool-enabled requests; the prompt-guided string path is where semantic-empties surface.
- Size max_tokens above the model's observed reasoning budget before serving tool turns.
- Keep one non-reasoning model last in every reliable chain to absorb think-only failures.
When it happens
Trigger: Calling chat() with a non-empty tools slice on a provider whose capabilities().native_tool_calling is false, where the string response strips to empty: reasoning models emitting only think tags, max_tokens exhausted before final text, or an upstream returning empty content with a success status.
Common situations: Pointing a prompt-guided-tools provider at a reasoning model (DeepSeek-R1 style), low max_tokens cutting output mid-reasoning, an OpenAI-compatible proxy stripping content blocks, model name typos that resolve to a reasoning-only variant.
Related errors
- provider completed without final text or tool calls
- provider completed without final text or tool calls
- provider completed without final text or tool calls
- provider completed without final text or tool calls
- provider completed without final text or tool calls
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/d383a6db1b886b3e.
Report an issue: GitHub.