Hmbown/CodeWhale · error

image inputs require a model with explicitly supported…

Error message

image inputs require a model with explicitly supported image input

What it means

Even with an exact model named, the runtime validates the resolved route's capabilities and fails the turn if the selected model's route does not declare image_input as CapabilityState::Supported. This is the capability check after routing, complementing the Auto-model checks.

Solutions

  1. Switch to a model whose route declares CapabilityState::Supported for image_input.
  2. Remove the image attachments from the turn.
  3. Fix the provider/route config so the intended model exposes image_input: supported.

Example fix

// before
req.model = "text-only-model".into();
req.prompt = blocks_with_image;
// after
req.model = "gpt-4o".into(); // image_input: Supported
req.prompt = blocks_with_image;
Defensive patterns

Strategy: validation

Validate before calling

let route = resolve_runtime_thread_route_for_identity(&cfg, &identity, Some(&model))?;
assert_eq!(route.candidate.capabilities().image_input, CapabilityState::Supported, "model cannot accept images");

Type guard

fn supports_images(cap: &CapabilityState) -> bool { *cap == CapabilityState::Supported }

Prevention

When it happens

Trigger: Sending image_blocks with an exact model whose resolved route candidate reports image_input != Supported (e.g. a text-only model like an o1-mini-style or small local model).

Common situations: Pointing the thread at a text-only model and attaching a screenshot; provider config drift where a model was previously multimodal; routing tables listing a model under a provider that strips vision support.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/ec8d7717840ded74. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/runtime_threads.rs:9917

            self.prepare_runtime_turn_operation(
                thread_id,
                Some(operation_key),
                request_fingerprint,
                reserved_turn_id,
            )?
        } else {
            None
        };
        if let Some(operation) = operation.as_ref()
            && let Some(original_turn) = self.replay_turn_for_operation(operation)?
        {
            return Ok((original_turn, true));
        }
        if !image_blocks.is_empty() || req.max_output_tokens.is_some() {
            let identity = self.provider_identity_for_thread(&cfg_snapshot, &thread)?;
            let route = resolve_runtime_thread_route_for_identity(&cfg_snapshot, &identity, Some(&requested_model))?;
            if !image_blocks.is_empty() && route.candidate.capabilities().image_input != codewhale_config::route::CapabilityState::Supported {
                bail!("image inputs require a model with explicitly supported image input");
            }
            if req.max_output_tokens.is_some() && !crate::route_budget::route_supports_output_token_limit(route.identity.provider, route.candidate.protocol()) {
                bail!("maxOutputTokens is unsupported by the selected provider transport");
            }
        }
        let engine = self.ensure_engine_loaded(&thread).await?;

        let client_preflight_required = {
            let active = self.active.lock().await;
            if let Some(active_thread) = active.engines.get(thread_id)
                && active_thread.active_turn.is_some()
            {
                bail!("Thread already has an active turn");
            }
            active
                .engines
                .get(thread_id)
                .is_none_or(|state| state.client_preflight_required)

View on GitHub (pinned to 73e0f67d83)