Hmbown/CodeWhale · warning · anyhow::Error

model must not be empty

Error message

model must not be empty

What it means

update_thread was called with Some(model) whose value is empty or whitespace-only. Model is the field that selects the provider route for the thread, and a blank model would silently resolve to a broken route, so it is rejected before any durable change.

Source

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

    pub async fn update_thread(&self, id: &str, req: UpdateThreadRequest) -> Result<ThreadRecord> {
        if req.archived.is_none()
            && req.allow_shell.is_none()
            && req.trust_mode.is_none()
            && req.auto_approve.is_none()
            && req.model.is_none()
            && req.mode.is_none()
            && req.permission_posture.is_none()
            && req.title.is_none()
            && req.system_prompt.is_none()
            && req.workspace.is_none()
        {
            bail!("At least one thread field is required");
        }

        if let Some(model) = req.model.as_ref()
            && model.trim().is_empty()
        {
            bail!("model must not be empty");
        }
        if let Some(mode) = req.mode.as_ref()
            && mode.trim().is_empty()
        {
            bail!("mode must not be empty");
        }
        if let Some(permission_posture) = req.permission_posture.as_ref()
            && permission_posture.trim().is_empty()
        {
            bail!("permission_posture must not be empty");
        }
        if let Some(workspace) = req.workspace.as_ref()
            && workspace.as_os_str().is_empty()
        {
            bail!("workspace must not be empty");
        }

        let configured_sandbox_mode = self.read_config().sandbox_mode.clone();

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Omit the model field (None) when the user clears the input instead of sending an empty string
  2. Trim input client-side and treat empty-as-unchanged in the UI layer
  3. If a model change is intended, send a valid model identifier from the configured registry
  4. Apply the same discipline to mode, permission_posture, and workspace - they have identical non-empty guards

Example fix

// before
let req = UpdateThreadRequest { model: Some(input.clone()), ..Default::default() }; // input == ""

// after
let req = UpdateThreadRequest {
    model: (!input.trim().is_empty()).then(|| input.trim().to_string()),
    ..Default::default()
};
Defensive patterns

Strategy: validation

Validate before calling

// Normalize: empty means 'omit the field'.
let req = UpdateThreadRequest {
    model: model_input.trim().is_empty().then(|| model_input.trim().to_string()),
    ..Default::default()
};
if req.model.is_none() && req.title.is_none() /* ... */ {
    return Ok(current); // nothing to change
}
manager.update_thread(id, req).await?;

Type guard

fn is_blank(value: &Option<String>) -> bool {
    value.as_ref().is_some_and(|v| v.trim().is_empty())
}

Prevention

When it happens

Trigger: UpdateThreadRequest { model: Some(""), .. } or Some(" ") - typically a form field cleared by the user, a trim-then-assign bug, or JSON that sends model: "" to mean 'unset'. Check at runtime_threads.rs:4642-4646 (sibling guards cover mode, permission_posture, workspace).

Common situations: A settings UI that serializes empty inputs as empty strings instead of omitting the key; migration scripts copying a missing model as ""; clients intending to clear/reset the model - the API has no 'clear', only 'set'.

Related errors


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