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
- Omit the model field (None) when the user clears the input instead of sending an empty string
- Trim input client-side and treat empty-as-unchanged in the UI layer
- If a model change is intended, send a valid model identifier from the configured registry
- 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
- Omit (None) fields the user cleared; the API has 'set', not 'clear'
- Trim strings and drop empty ones before building the request
- The same non-empty rule applies to mode, permission_posture, and workspace
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
- At least one thread field is required
- Automation name cannot be empty
- Automation prompt cannot be empty
- Trigger message must not be empty
- Antigravity cloud-code does not accept role {other:?}
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/652f0a34cea13e09.
Report an issue: GitHub.