zeroclaw-labs/zeroclaw · critical · anyhow::Error
ModelProvider returned non-prompt-guided tools payload ({pay
Error message
ModelProvider returned non-prompt-guided tools payload ({payload:?}) while supports_native_tools() is false What it means
Second step of tool_linker_http(): crate::component::add_wasi_http attaches the wasi:http host functions to the base linker so HttpClient-granted plugins can make network calls. The expect fires when the wasi:http surface cannot be attached — again almost always crate-version mismatch between wasmtime-wasi-http and the engine, or duplicate import definitions on the linker. Cached in a OnceLock, so the first HTTP-capable plugin pays the panic.
Source
Thrown at crates/zeroclaw-api/src/model_provider.rs:653
.await
}
/// Structured chat API for agent loop callers. See `simple_chat` for
/// the `temperature` contract.
async fn chat(
&self,
request: ChatRequest<'_>,
model: &str,
temperature: Option<f64>,
) -> anyhow::Result<ChatResponse> {
if let Some(tools) = request.tools
&& !tools.is_empty()
&& !self.supports_native_tools()
{
let tool_instructions = match self.convert_tools(tools) {
ToolsPayload::PromptGuided { instructions } => instructions,
payload => {
anyhow::bail!(
"ModelProvider returned non-prompt-guided tools payload ({payload:?}) while supports_native_tools() is false"
)
}
};
let mut modified_messages = request.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(&tool_instructions);
} else {
modified_messages.insert(0, ChatMessage::system(tool_instructions));
}
let text = self
.chat_with_history(&modified_messages, model, temperature)View on GitHub (pinned to 88bb9c8533)
Solutions
- Update wasmtime-wasi-http to the same version as wasmtime and wasmtime-wasi, then cargo clean and rebuild.
- Inspect Cargo.lock for mixed wasmtime* versions (`grep -A1 'name = "wasmtime' Cargo.lock`).
- Regenerate wasi:http bindings from the same wit snapshot the host crates target.
- Add a create_plugin smoke test for an HTTP-granted plugin to CI so this fails at build time, not in production.
Example fix
// before
crate::component::add_wasi_http(&mut linker).expect("tool http linker");
// after
crate::component::add_wasi_http(&mut linker)
.map_err(|e| anyhow::anyhow!("wasi:http linker init failed: {e:#}"))?; Defensive patterns
Strategy: fallback
Try / catch
// Contain the wasi:http linker panic at the plugin host boundary:
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
futures::executor::block_on(create_plugin(&wasm_path, &http_scope, limits))
}));
match result {
Err(_) => { /* disable HttpClient capability, run plugin without network */ }
Ok(_) => { /* proceed */ }
} Prevention
- Verify `cargo tree -i wasmtime-wasi-http` shows a single version
- Include an HttpClient-granted plugin in CI smoke tests
- Regenerate wasi:http bindings from the same wit snapshot as the base world
- Treat any wasmtime linker panic as a build/environment defect, not a runtime data issue
When it happens
Trigger: First instantiation of an HttpClient-capable plugin with wasmtime-wasi-http version skew, or a fork that already defined wasi:http imports on the linker before add_wasi_http runs.
Common situations: wasmtime-wasi-http left on an old version while wasmtime was upgraded; vendored bindings out of sync with the host crates; plugins rebuilt with a newer wit toolchain than the host supports.
Related errors
- live model listing is not supported for this model_provider
- memory backend '{}' does not support agent-attributed StoreO
- purge_agent not supported by this memory backend
- rename_agent not supported by this memory backend
- memory backend '{}' does not support StoreOptions kind/pinne
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/b2449504f77c9c08.
Report an issue: GitHub.