Hmbown/CodeWhale · error

permission_posture must not be empty

Error message

permission_posture must not be empty

What it means

When a thread-update request includes a `permission_posture`, the value must not be empty or whitespace-only. The posture drives permission decisions for the thread, so a blank value would silently disable or corrupt permission policy.

Solutions

  1. Pass `None` to leave the current posture unchanged.
  2. Use one of the library's named postures rather than a free-form string.
  3. Trim-and-drop or validate against the supported posture set before sending.

Example fix

// before
UpdateThreadRequest { permission_posture: Some(posture), .. } // may be ""

// after
UpdateThreadRequest { permission_posture: Some(posture).filter(|p| !p.trim().is_empty()), .. }
Defensive patterns

Strategy: validation

Validate before calling

if let Some(p) = &req.permission_posture { if p.trim().is_empty() { req.permission_posture = None; } }

Type guard

fn valid_posture(p: &str) -> bool { !p.trim().is_empty() }

Prevention

When it happens

Trigger: Calling update-thread with `permission_posture: Some("".into())` or a whitespace-only string; a client sends `{"permission_posture": ""}`.

Common situations: A settings UI clears a posture dropdown; automation copies an empty posture field from another thread; a config merge produces empty strings for unset enum-like fields.

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/8fd36ed9e616ee1e. Report an issue: GitHub.

Appendix: source

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

            && 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);
                self.validate_shell_access_policy(&workspace, config_path, config_profile)
                    .await?;
                Some(workspace)
            } else {
                None

View on GitHub (pinned to 73e0f67d83)