Hmbown/CodeWhale · error

Route preference must be nonempty and contain no control…

Error message

Route preference must be nonempty and contain no control characters

What it means

After key validation, set_document trims the value and rejects values that are empty or contain control characters, since route preferences are identifiers/names written into TOML and a blank or control-bearing value would corrupt the config or silently clear the selection.

Solutions

  1. Trim the value and ensure it is nonempty before calling
  2. Strip control characters from user input before persisting
  3. Validate the model/provider name format upstream of the setter

Example fix

// before
set(path, "model", "\n")?;
// after
let v = raw.trim();
anyhow::ensure!(!v.is_empty() && !v.chars().any(char::is_control));
set(path, "model", v)?;
Defensive patterns

Strategy: validation

Validate before calling

let v = value.trim();
if v.is_empty() || v.chars().any(char::is_control) { return Err(anyhow!("route value must be nonempty, control-char free")); }

Type guard

fn is_clean_nonempty(s: &str) -> bool { !s.trim().is_empty() && !s.chars().any(char::is_control) }

Prevention

When it happens

Trigger: Calling set()/set_document() with value "" or a value containing control chars (e.g. stray \n, \t, or terminal escape bytes from clipboard paste).

Common situations: Unset environment variables interpolated empty in shell scripts; copy-paste of model names with hidden control characters; automated edits passing blank placeholders.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/609bd869e589839d. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/route_preferences.rs:362

    let mut doc = raw
        .parse::<toml_edit::DocumentMut>()
        .map_err(|_| anyhow::anyhow!("Could not parse route configuration; contents omitted"))?;
    parse_config(raw)?;
    persistence::migrate_legacy_route_preferences(path, &mut doc)?;
    Ok(doc)
}

/// Edit one route selection in an already-prepared candidate without saving.
/// Callers must prepare migration first and atomically save the final snapshot.
pub fn set_document(
    path: &Path,
    doc: &mut toml_edit::DocumentMut,
    key: &str,
    value: &str,
) -> Result<()> {
    ensure!(is_route_key(key), "Not a route preference key: {key}");
    let value = value.trim();
    ensure!(
        !value.is_empty() && !value.chars().any(char::is_control),
        "Route preference must be nonempty and contain no control characters"
    );
    if let Some(key) = project_root_key(path, key) {
        return persistence::set_document_value(doc, &[key], value);
    }
    let config = parse_config(&doc.to_string())?;
    if key == "provider" {
        let identity = config
            .resolve_provider_pin_identity(value)
            .map_err(anyhow::Error::msg)?;
        persistence::set_document_value(
            doc,
            &["provider"],
            identity.persisted_id().unwrap_or(&identity.key),
        )?;
        // Same root-alias authority as the Runtime/TUI provider writer: a CLI
        // switch must not leave the incoming route holding the outgoing one's

View on GitHub (pinned to 73e0f67d83)