Hmbown/CodeWhale · error
model must not be empty
Error message
model must not be empty
What it means
When a thread-update request includes a `model`, the value must not be whitespace-only/empty. The guard rejects `Some("")` or `Some(" ")` so a thread never ends up with a meaningless model assignment.
Solutions
- Pass `None` to leave the model unchanged instead of `Some("")`.
- Trim the incoming string and, if empty after trim, drop the field from the request.
- Validate the model string against the registry before sending the update.
Example fix
// before
UpdateThreadRequest { model: Some(user_input), .. }
// after
let model = user_input.trim().to_string();
UpdateThreadRequest { model: (!model.is_empty()).then_some(model), .. } Defensive patterns
Strategy: validation
Validate before calling
let model = req.model.as_deref().map(str::trim).filter(|m| !m.is_empty());
Type guard
fn valid_model_field(m: &Option<String>) -> bool {
m.as_deref().map_or(true, |s| !s.trim().is_empty())
} Prevention
- Normalize empty strings to None at the client boundary
- Validate model names against the registry before updates
- Never serialize cleared form fields as "" — omit them
When it happens
Trigger: Calling update-thread with `model: Some("".into())` or `Some(" \t".into())`; a client form submitted with the model field cleared but still serialized as an empty string.
Common situations: An UI clears the model dropdown and sends "" instead of `null`; a config export round-trip loses the model value; JSON body `{"model": ""}` from an API consumer.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- At least one thread field is required
- empty key text
- maxOutputTokens requires an exact model
- mode must not be empty
- permission_posture must not be empty
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/652f0a34cea13e09.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_threads.rs:7882
let _config_admission = self.config_admission.read().await;
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");
}
// Source resolution reads config files. Do it off the Tokio worker,View on GitHub (pinned to 73e0f67d83)