{"record":{"id":"d02ed84d0551c7e4","repo":"Hmbown/CodeWhale","slug":"model-client-should-be-configured","errorCode":null,"errorMessage":"model client should be configured","messagePattern":"model client should be configured","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/core/engine/turn_loop.rs","lineNumber":563,"sourceCode":"        &mut self,\n        turn: &mut TurnContext,\n        tool_policy: ToolSurfacePolicy,\n        // Out-of-request facts resolved once for this turn. `None` means the\n        // caller captured none, and the projection reports every\n        // registry-derived field as unknown rather than guessing.\n        inspection_surface: Option<crate::tool_inspection::ToolSurfaceContext>,\n    ) -> (TurnOutcomeStatus, Option<String>) {\n        // Only interactive TUI hosts own terminal chrome. Headless exec,\n        // app-server, and stream-json stdout must remain byte-clean.\n        if self.config.terminal_chrome_enabled {\n            crate::tui::notifications::set_taskbar_progress_busy();\n            crate::tui::notifications::start_title_animation(\"Codewhale\");\n        }\n\n        let client = self\n            .model_client\n            .clone()\n            .expect(\"model client should be configured\");\n\n        let mut turn_error: Option<String> = None;\n        // Cleared when the loop continues only for optional runtime work\n        // (a goal continuation) after the model already delivered an answer.\n        let mut step_budget_exhaustion_is_terminal = true;\n        let mut context_recovery_attempts = 0u8;\n        let mut tool_policy = tool_policy;\n        let mut mode = tool_policy.mode;\n        let mut questions_allowed = tool_policy.allows_questions();\n        let strict_tool_mode = tool_policy.strict_tool_mode;\n        let mut tool_catalog = std::mem::take(&mut tool_policy.catalog);\n        let mut active_tool_names = std::mem::take(&mut tool_policy.active_names);\n        // Search activations belong to the conversation, not just the user\n        // turn. Revalidate names against this turn's already-filtered catalog\n        // before exposing them; stale mode/MCP/allow-list entries disappear.\n        let evicted = self.session.tool_activation_cache.revalidate(&tool_catalog);\n        super::tool_catalog::remove_evicted_cache_activations(\n            &tool_catalog,","sourceCodeStart":545,"sourceCodeEnd":581,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/core/engine/turn_loop.rs#L545-L581","documentation":"consult_auto_review_guardian() unwraps access to the turn's model client, asserting the turn loop only reaches this point after the client was configured. Hitting the expect means a turn was started without a model client — an engine state-machine bug, not user input.","triggerScenarios":"Thrown at crates/tui/src/core/engine/turn_loop.rs:563 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Ensure the turn is aborted earlier when no model client is configured","Trace how run_turn was reached with an unset client and fix the guard upstream"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}