Hmbown/CodeWhale · error

custom_models[ ]. is invalid

Error message

custom_models[{index}].{field} is invalid

What it means

`validate_configured_models` enforces structural rules on each entry of the `custom_models` config list: `provider` and `id` must be non-empty, at most 256 chars, and free of whitespace and control characters. The error names the offending entry index and field but deliberately never echoes the invalid value, to avoid leaking untrusted content.

Solutions

  1. Open the config file and inspect `custom_models[index]` — fix the field named in the message.
  2. Ensure both `provider` and `id` are non-empty, ≤256 chars, with no spaces or control characters.
  3. Trim surrounding whitespace from pasted values and re-run the command.

Example fix

// before (config TOML)
[[custom_models]]
provider = "openai "
id = "gpt-4o"
// after
[[custom_models]]
provider = "openai"
id = "gpt-4o"
Defensive patterns

Strategy: validation

Validate before calling

fn valid_field(s: &str) -> bool {
    !s.is_empty() && s.len() <= 256
        && !s.chars().any(char::is_whitespace)
        && !s.chars().any(char::is_control)
}
// run on each custom_models entry before writing config
assert!(valid_field(&model.provider) && valid_field(&model.id));

Prevention

When it happens

Trigger: Deserializing config via `deserialize_configured_models` when a `custom_models[i].provider` or `.id` is empty, longer than 256 characters, or contains spaces/tabs/control characters.

Common situations: Hand-edited config with a trailing space in the model id, a pasted provider name containing a newline, or a copy-paste that left the field blank.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at crates/config/src/catalog/configured.rs:73

            endpoint_key: "chat".into(),
            limit: self.limit.clone(),
            cost: self.cost.clone(),
            modalities: self.modalities.clone(),
            attachment: self.attachment,
            reasoning: self.reasoning,
            tool_call: self.tool_call,
            structured_output: self.structured_output,
            source: CatalogSource::ConfigOverride,
            ..CatalogOffering::default()
        }
    }
}

pub fn validate_configured_models(models: &[ConfiguredModel]) -> anyhow::Result<()> {
    let mut identities = BTreeSet::new();
    for (index, model) in models.iter().enumerate() {
        // Error messages name fields, never echo untrusted values or URLs.
        let invalid = |field| anyhow::anyhow!("custom_models[{index}].{field} is invalid");
        for (field, value) in [("provider", &model.provider), ("id", &model.id)] {
            if value.is_empty()
                || value.len() > 256
                || value.chars().any(char::is_whitespace)
                || value.chars().any(char::is_control)
            {
                return Err(invalid(field));
            }
        }
        if model.id.eq_ignore_ascii_case("auto") {
            return Err(invalid("id"));
        }
        let Some((scheme, rest)) = model.base_url.split_once("://") else {
            return Err(invalid("base_url"));
        };
        if !matches!(scheme.to_ascii_lowercase().as_str(), "http" | "https")
            || rest.split('/').next().is_none_or(str::is_empty)
            || model.base_url.contains(['@', '?', '#'])

View on GitHub (pinned to 73e0f67d83)