zeroclaw-labs/zeroclaw · error · anyhow::Error
Ollama returned non-prompt-guided tools payload while native
Error message
Ollama returned non-prompt-guided tools payload while native tools are disabled
What it means
The Ollama provider hard-codes supports_native_tools() = false, so chat() injects tool specifications into the system prompt as text via with_prompt_guided_tool_instructions, which requires convert_tools to return ToolsPayload::PromptGuided. This bail fires when a non-empty tools slice converts to a native payload variant (OpenAI/Anthropic/Gemini) instead. Stock builds cannot reach it — OllamaModelProvider never overrides the trait's default PromptGuided conversion — so encountering it means a wrapper or fork changed tool conversion without enabling native tools.
Source
Thrown at crates/zeroclaw-providers/src/ollama.rs:604
images: None,
tool_calls: None,
tool_name: None,
}
})
.collect()
}
fn with_prompt_guided_tool_instructions(
&self,
messages: &[ChatMessage],
tools: Option<&[zeroclaw_api::tool::ToolSpec]>,
) -> anyhow::Result<Vec<ChatMessage>> {
let Some(tools) = tools.filter(|items| !items.is_empty()) else {
return Ok(messages.to_vec());
};
let ToolsPayload::PromptGuided { instructions } = self.convert_tools(tools) else {
anyhow::bail!(
"Ollama returned non-prompt-guided tools payload while native tools are disabled"
);
};
let mut modified_messages = messages.to_vec();
if let Some(system_message) = modified_messages.iter_mut().find(|m| m.role == "system") {
if !system_message.content.is_empty() {
system_message.content.push_str("\n\n");
}
system_message.content.push_str(&instructions);
} else {
modified_messages.insert(0, ChatMessage::system(instructions));
}
Ok(modified_messages)
}
fn response_to_chat_response(&self, response: ApiChatResponse, model: &str) -> ChatResponse {View on GitHub (pinned to 88bb9c8533)
Solutions
- If you maintain a wrapper: do not override convert_tools for Ollama, or keep returning ToolsPayload::PromptGuided while native tools are disabled
- If native Ollama tools are intended, implement the full path — supports_native_tools() = true plus tools serialization in the request body — instead of half-enabling it
- Otherwise report it upstream with the wrapper code, since the shipped provider cannot produce a native payload
Example fix
// before (fork/wrapper)
fn convert_tools(&self, tools: &[ToolSpec]) -> ToolsPayload {
ToolsPayload::OpenAI { tools: to_openai(tools) }
}
// after: keep prompt-guided while native tools are disabled
fn convert_tools(&self, tools: &[ToolSpec]) -> ToolsPayload {
ToolsPayload::PromptGuided { instructions: build_tool_instructions_text(tools) }
} Defensive patterns
Strategy: type-guard
Validate before calling
fn ollama_tools_prompt_guided_safe(
provider: &dyn ModelProvider,
tools: &[zeroclaw_api::tool::ToolSpec],
) -> bool {
if tools.is_empty() { return true; }
!provider.supports_native_tools()
&& matches!(provider.convert_tools(tools), ToolsPayload::PromptGuided { .. })
} Type guard
fn tools_payload_is_prompt_guided(payload: &ToolsPayload) -> bool {
matches!(payload, ToolsPayload::PromptGuided { .. })
} Try / catch
let messages = if !tools.is_empty() {
match provider.convert_tools(tools) {
ToolsPayload::PromptGuided { .. } => inject_instructions(messages, tools),
_ => return Err(anyhow::anyhow!("wrapper overrides convert_tools with native tools disabled")),
}
} else { messages.to_vec() }; Prevention
- Never override convert_tools on a provider whose supports_native_tools() is false
- If you enable native tool payloads, flip supports_native_tools() and serialize tools in the same change
- Wrap third-party providers with a guard that asserts the PromptGuided variant before chat()
When it happens
Trigger: chat() with a non-empty request.tools on a modified Ollama provider whose convert_tools override returns a native ToolsPayload variant; downstream forks of ollama.rs enabling native conversion without also flipping supports_native_tools and serializing tools in send_request.
Common situations: Custom ModelProvider wrappers or vision/model-pin style decorators that alter convert_tools; forks porting native tool-calling support from another provider halfway; upstream refactors changing the default trait implementation.
Related errors
- needs_reassembly implies a step agent alias
- owned implies a reassembly handle
- Schema missing required 'type' field
- desired command set is not an array
- providers.models.ollama.{alias}.model uses ':cloud', but uri
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/a4f888885593e77b.
Report an issue: GitHub.