Hmbown/CodeWhale · error
mode must not be empty
Error message
mode must not be empty
What it means
When a thread-update request includes a `mode`, the value must be a non-empty, non-whitespace string. Empty mode strings are rejected so threads never carry an unset/blank mode that downstream policy checks would misinterpret.
Solutions
- Pass `None` for `mode` to keep the current mode unchanged.
- Validate the mode is one of the supported non-empty values before calling the API.
- Trim and drop the field if the input is blank.
Example fix
// before
UpdateThreadRequest { mode: Some(mode_field), .. } // mode_field may be ""
// after
UpdateThreadRequest { mode: Some(mode_field).filter(|m| !m.trim().is_empty()), .. } Defensive patterns
Strategy: validation
Validate before calling
if let Some(mode) = &req.mode { if mode.trim().is_empty() { req.mode = None; } } Type guard
fn valid_mode(m: &str) -> bool { !m.trim().is_empty() } Prevention
- Constrain mode inputs to a fixed enum/list in the UI
- Trim user input before building requests
- Send None (field omitted) rather than empty strings
When it happens
Trigger: Calling update-thread with `mode: Some("".into())` or whitespace-only; deserializing a JSON body like `{"mode": ""}`.
Common situations: A mode selector resets to blank; an integration forwards a mode field from an external source where it was empty; a migration script writes empty strings instead of null.
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
- empty key text
- model must not be empty
- permission_posture must not be empty
- 1
- A pinned task provider requires an explicit model
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/60836c553757d1bc.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/runtime_threads.rs:7887
&& 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,
// then recheck the conversation identity before committing the grant.
let shell_policy_workspace = if req.allow_shell == Some(true) || req.workspace.is_some() {
let current = self.get_thread(id).await?;
if req.allow_shell.unwrap_or(current.allow_shell) {
let workspace = req.workspace.clone().unwrap_or(current.workspace);View on GitHub (pinned to 73e0f67d83)