{"record":{"id":"72339b894f765109","repo":"Hmbown/CodeWhale","slug":"provider-model-model-uses-candidate-protocol-but-this-client-72339b","errorCode":null,"errorMessage":"{provider} model {model} uses {candidate_protocol}, but this client is bound to {wire_format} and no configuration is available to rebuild it","messagePattern":"(.+?) model (.+?) uses (.+?), but this client is bound to (.+?) and no configuration is available to rebuild it","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":1817,"sourceCode":"        if candidate.protocol() == self.wire_format\n            && candidate.wire_model_id().as_str() == self.default_model\n            && candidate_limits == self.route_limits\n        {\n            return Ok(None);\n        }\n        if candidate.protocol() == self.wire_format {\n            // Same-protocol model switches keep the already-authenticated,\n            // endpoint-bound transport but must freeze the alternate model's\n            // exact wire identity and limits. This path is also what makes a\n            // model-aware client usable in embedded/test runtimes that do not\n            // retain the original Config after construction.\n            let mut rebound = self.clone();\n            rebound.default_model = candidate.wire_model_id().as_str().to_string();\n            rebound.route_limits = candidate_limits;\n            return Ok(Some(rebound));\n        }\n        let config = config.ok_or_else(|| {\n            anyhow::anyhow!(\n                \"{} model {:?} uses {:?}, but this client is bound to {:?} and no configuration is available to rebuild it\",\n                self.api_provider.display_name(),\n                model,\n                candidate.protocol(),\n                self.wire_format\n            )\n        })?;\n        let mut rebound = Self::from_candidate(config, &candidate)?;\n        rebound.configured_models = Arc::clone(&self.configured_models);\n        Ok(Some(rebound))\n    }\n\n    fn bind_request_to_protocol(\n        &self,\n        mut request: MessageRequest,\n    ) -> Result<(MessageRequest, Option<RouteLimits>)> {\n        let model_aware = self.api_provider.metadata().is_some_and(|provider| {\n            provider.wire_policy() == codewhale_config::provider::WirePolicy::ModelAware","sourceCodeStart":1799,"sourceCodeEnd":1835,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client.rs#L1799-L1835","documentation":"rebound_for_model_protocol resolves the requested model through the route resolver; when the resolved protocol differs from the wire format this client was built with, the client must be rebuilt from the original Config. This error is raised when a cross-protocol model switch is requested but the `config` argument is None, so no rebuild is possible. It exists to guarantee a client never silently sends requests in the wrong wire dialect (e.g. Anthropic Messages payload to a Chat Completions endpoint).","triggerScenarios":"Calling rebound_for_model_protocol(config=None, model=...) where the resolved route for `model` uses a different protocol than self.wire_format (e.g. an AnthropicMessages-bound client asked for a ChatCompletions model). The same-protocol path (client.rs:1805) succeeds without config; only cross-protocol switches hit this error.","commonSituations":"Embedded or test runtimes that construct a model-aware client and drop the original Config before switching models; switching to a fallback model that lives on a provider with a different wire protocol; a route/mid-session model change in a runtime that never retained Config.","solutions":["Pass the original Config reference when calling rebound_for_model_protocol instead of None","Retain the Config for the client's lifetime if mid-session model switching is expected","Pick a model that resolves to the same protocol as the current binding, or construct a fresh client for the other protocol","If a fallback chain triggers this, ensure fallback models resolve to compatible protocols or rebuild the client at fallback time with the config available"],"exampleFix":"// before\nlet rebound = client.rebound_for_model_protocol(None, \"claude-sonnet-4\")?;\n\n// after\nlet rebound = client.rebound_for_model_protocol(Some(&config), \"claude-sonnet-4\")?;","handlingStrategy":"fallback","validationCode":"// Before switching models mid-session, confirm the target protocol is reachable:\nlet candidate = client.resolve_model_route(model)?;\nlet cross_protocol = candidate.protocol() != client.wire_format;\nif cross_protocol && config.is_none() {\n    eprintln!(\"model {model} needs a protocol rebind; keep Config alive or pick a same-protocol model\");\n}\n","typeGuard":null,"tryCatchPattern":"match client.rebound_for_model_protocol(config.as_ref(), &model) {\n    Ok(Some(rebound)) => { /* use rebound */ }\n    Ok(None) => { /* current binding already exact */ }\n    Err(e) if e.to_string().contains(\"no configuration is available\") => {\n        // retain/restore Config and retry, or reject the model switch with a clear user message\n    }\n    Err(e) => return Err(e),\n}\n","preventionTips":["Keep the original Config alive for the client's lifetime when model switching is a feature","Route same-protocol model changes only, or pre-build one client per protocol at startup","In embedded/test runtimes, always pass Some(config) to rebound_for_model_protocol","Check candidate.protocol() against the client binding before offering a model switch in the UI"],"tags":["routing","protocol","client-rebind"],"backgroundTag":"missing-required-argument","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}