Hmbown/CodeWhale · error

model must be nonempty and contain no control characters

Error message

model must be nonempty and contain no control characters

What it means

set_provider_model_document writes a provider's model into the TOML document, but first validates the model string: it must be non-empty after trimming and contain no control characters, since control characters would corrupt the config file or downstream requests. This ensure! rejects empty, whitespace-only, or control-character-bearing model names.

Solutions

  1. Trim and validate the model string before calling any persistence API; reject empty/control chars at the input boundary
  2. Fix the upstream source of the model name (env var, config field, model list) so it supplies a real model id
  3. Sanitize pasted/CLI model identifiers by stripping whitespace and rejecting control characters

Example fix

// before
persist_provider_model_key(provider, identity, "\n")?;
// after
let model = raw_model.trim();
anyhow::ensure!(!model.is_empty() && !model.chars().any(char::is_control));
persist_provider_model_key(provider, identity, model)?;
Defensive patterns

Strategy: validation

Validate before calling

fn valid_model_name(m: &str) -> bool {
    let t = m.trim();
    !t.is_empty() && !m.chars().any(char::is_control)
}
// gate every persistence call on valid_model_name(model)

Try / catch

match persist_provider_model_key(provider, identity, model) {
    Err(e) if e.to_string().contains("model must be nonempty") => {
        reprompt_for_model();
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling any of the callers (persist_provider_selection, persist_provider_model_key, migrate_legacy_route_preferences, reconcile_root_model_aliases) with model == "", a whitespace string, or a string containing \n, \t, \r or other control chars.

Common situations: A model field read from a broken input source (empty env var, blank UI field); pasting a model id with embedded newline; upstream listing code returning empty strings for missing models.

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/5f0793511936f886. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/config_persistence.rs:163

            }
            set_provider_model_document(
                doc,
                identity.provider,
                identity.persisted_id().unwrap_or(&identity.key),
                model,
            )?;
        }
    }
    set_document_value(doc, &["route_preferences_version"], 1_i64)
}

pub(crate) fn set_provider_model_document(
    doc: &mut toml_edit::DocumentMut,
    provider: ApiProvider,
    provider_identity: &str,
    model: &str,
) -> anyhow::Result<()> {
    anyhow::ensure!(
        !model.trim().is_empty() && !model.chars().any(char::is_control),
        "model must be nonempty and contain no control characters"
    );
    let config: crate::config::Config = toml::from_str(&doc.to_string()).map_err(|_| {
        anyhow::anyhow!("Could not parse destination route identity; contents omitted")
    })?;
    let identity = config
        .resolve_provider_pin_identity(provider_identity)
        .map_err(anyhow::Error::msg)?;
    anyhow::ensure!(
        identity.provider == provider,
        "The destination config has a different provider identity"
    );
    let provider_key = if provider == ApiProvider::Custom {
        if identity.persisted_id().is_none() {
            return set_document_value(doc, &["default_text_model"], model);
        }
        identity.key

View on GitHub (pinned to 73e0f67d83)