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

  1. Pass `None` for `mode` to keep the current mode unchanged.
  2. Validate the mode is one of the supported non-empty values before calling the API.
  3. 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

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


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)