{"record":{"id":"b2449504f77c9c08","repo":"zeroclaw-labs/zeroclaw","slug":"modelprovider-returned-non-prompt-guided-tools-pay","errorCode":null,"errorMessage":"ModelProvider returned non-prompt-guided tools payload ({payload:?}) while supports_native_tools() is false","messagePattern":"ModelProvider returned non-prompt-guided tools payload \\((.+?)\\) while supports_native_tools\\(\\) is false","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/zeroclaw-api/src/model_provider.rs","lineNumber":653,"sourceCode":"            .await\n    }\n\n    /// Structured chat API for agent loop callers. See `simple_chat` for\n    /// the `temperature` contract.\n    async fn chat(\n        &self,\n        request: ChatRequest<'_>,\n        model: &str,\n        temperature: Option<f64>,\n    ) -> anyhow::Result<ChatResponse> {\n        if let Some(tools) = request.tools\n            && !tools.is_empty()\n            && !self.supports_native_tools()\n        {\n            let tool_instructions = match self.convert_tools(tools) {\n                ToolsPayload::PromptGuided { instructions } => instructions,\n                payload => {\n                    anyhow::bail!(\n                        \"ModelProvider returned non-prompt-guided tools payload ({payload:?}) while supports_native_tools() is false\"\n                    )\n                }\n            };\n            let mut modified_messages = request.messages.to_vec();\n\n            if let Some(system_message) = modified_messages.iter_mut().find(|m| m.role == \"system\")\n            {\n                if !system_message.content.is_empty() {\n                    system_message.content.push_str(\"\\n\\n\");\n                }\n                system_message.content.push_str(&tool_instructions);\n            } else {\n                modified_messages.insert(0, ChatMessage::system(tool_instructions));\n            }\n\n            let text = self\n                .chat_with_history(&modified_messages, model, temperature)","sourceCodeStart":635,"sourceCodeEnd":671,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-api/src/model_provider.rs#L635-L671","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncrate::component::add_wasi_http(&mut linker).expect(\"tool http linker\");\n\n// after\ncrate::component::add_wasi_http(&mut linker)\n    .map_err(|e| anyhow::anyhow!(\"wasi:http linker init failed: {e:#}\"))?;","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Contain the wasi:http linker panic at the plugin host boundary:\nlet result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    futures::executor::block_on(create_plugin(&wasm_path, &http_scope, limits))\n}));\nmatch result {\n    Err(_) => { /* disable HttpClient capability, run plugin without network */ }\n    Ok(_) => { /* proceed */ }\n}","preventionTips":["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"],"tags":["rust","wasmtime","wasi-http","plugins","version-mismatch","panic"],"backgroundTag":"wasmtime-linker-init-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}