Hmbown/CodeWhale · warning · std::io::Error

Session title cannot be empty

Error message

Session title cannot be empty

What it means

normalize_session_title returns InvalidInput when a session title is empty after sanitize_session_title and trim. Every rename surface (the session picker, the /rename command, and PATCH /v1/sessions/{id}) funnels through this one function, so all of them reject the same input with the same message.

Source

Thrown at crates/tui/src/session_manager.rs:1911

/// OSC 0 terminal title, `codewhale sessions` stdout, and the picker, so the
/// persisted value must not be able to carry a raw escape sequence. Ordinary
/// text, punctuation, CJK, and emoji pass through untouched.
pub fn sanitize_session_title(raw: &str) -> String {
    raw.chars()
        .filter(|ch| !ch.is_control() && !is_title_format_char(*ch))
        .collect()
}

/// Sanitize, trim, and bound a user-supplied session title.
///
/// Returns `InvalidInput` for an empty title or one longer than
/// [`MAX_SESSION_TITLE_CHARS`] so every rename surface (picker, `/rename`,
/// `PATCH /v1/sessions/{id}`) rejects the same inputs with the same reason.
pub fn normalize_session_title(title: &str) -> std::io::Result<String> {
    let sanitized = sanitize_session_title(title);
    let trimmed = sanitized.trim();
    if trimmed.is_empty() {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            "Session title cannot be empty",
        ));
    }
    if trimmed.chars().count() > MAX_SESSION_TITLE_CHARS {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            format!("Session title cannot exceed {MAX_SESSION_TITLE_CHARS} characters"),
        ));
    }
    Ok(trimmed.to_string())
}

pub(crate) fn workspace_scope_matches(saved_workspace: &Path, current_workspace: &Path) -> bool {
    if paths_equivalent(saved_workspace, current_workspace) {
        return true;
    }

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Send a title containing at least one printable, non-control character
  2. If you control the caller, run trim (and the same sanitization) before submitting so you can give the user a better message than the server error
  3. Check where the empty string comes from — often a template variable or unset field defaulting to blank

Example fix

// before
let title = normalize_session_title("   \u{0}")?; // InvalidInput

// after
let cleaned = sanitize_session_title(raw).trim().to_string();
if cleaned.is_empty() {
    return Ok(fallback_title()); // or prompt the user again
}
let title = normalize_session_title(&cleaned)?;
Defensive patterns

Strategy: validation

Validate before calling

let candidate = sanitize_session_title(raw).trim();
if candidate.is_empty() {
    // reject in the UI/form before calling any rename API
    return Err(own_error("title is required"));
}

Type guard

fn is_empty_title_error(e: &std::io::Error) -> bool {
    e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains("cannot be empty")
}

Try / catch

match normalize_session_title(&raw) {
    Ok(t) => rename(id, &t).await,
    Err(e) if is_empty_title_error(&e) => show_field_error("title", "Enter a non-empty title"),
    Err(e) => show_field_error("title", &e.to_string()),
}

Prevention

When it happens

Trigger: Calling a rename API with a title of "", whitespace-only input, or a title made entirely of characters the sanitizer strips (control characters, zero-width characters), so nothing printable remains after sanitize+trim.

Common situations: A web/API client submits a form with whitespace only; caller validates length before sanitization and misses that sanitization empties the string; double-trim or templating bug produces an empty default title.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/c88103c7d8509979. Report an issue: GitHub.