Hmbown/CodeWhale · error

model client should be configured

Error message

model client should be configured

What it means

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.

Source

Thrown at crates/tui/src/core/engine/turn_loop.rs:563

        &mut self,
        turn: &mut TurnContext,
        tool_policy: ToolSurfacePolicy,
        // Out-of-request facts resolved once for this turn. `None` means the
        // caller captured none, and the projection reports every
        // registry-derived field as unknown rather than guessing.
        inspection_surface: Option<crate::tool_inspection::ToolSurfaceContext>,
    ) -> (TurnOutcomeStatus, Option<String>) {
        // Only interactive TUI hosts own terminal chrome. Headless exec,
        // app-server, and stream-json stdout must remain byte-clean.
        if self.config.terminal_chrome_enabled {
            crate::tui::notifications::set_taskbar_progress_busy();
            crate::tui::notifications::start_title_animation("Codewhale");
        }

        let client = self
            .model_client
            .clone()
            .expect("model client should be configured");

        let mut turn_error: Option<String> = None;
        // Cleared when the loop continues only for optional runtime work
        // (a goal continuation) after the model already delivered an answer.
        let mut step_budget_exhaustion_is_terminal = true;
        let mut context_recovery_attempts = 0u8;
        let mut tool_policy = tool_policy;
        let mut mode = tool_policy.mode;
        let mut questions_allowed = tool_policy.allows_questions();
        let strict_tool_mode = tool_policy.strict_tool_mode;
        let mut tool_catalog = std::mem::take(&mut tool_policy.catalog);
        let mut active_tool_names = std::mem::take(&mut tool_policy.active_names);
        // Search activations belong to the conversation, not just the user
        // turn. Revalidate names against this turn's already-filtered catalog
        // before exposing them; stale mode/MCP/allow-list entries disappear.
        let evicted = self.session.tool_activation_cache.revalidate(&tool_catalog);
        super::tool_catalog::remove_evicted_cache_activations(
            &tool_catalog,

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Ensure the turn is aborted earlier when no model client is configured
  2. Trace how run_turn was reached with an unset client and fix the guard upstream
Defensive patterns

Strategy: type-guard

When it happens

Trigger: Thrown at crates/tui/src/core/engine/turn_loop.rs:563 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/d02ed84d0551c7e4. Report an issue: GitHub.