Hmbown/CodeWhale · error
custom provider name may only use letters, numbers, '-' and…
Error message
custom provider name may only use letters, numbers, '-' and '_'
What it means
Custom provider names are restricted to ASCII alphanumerics, underscores, and hyphens so they form safe TOML table keys and identifiers. Any other character (space, dot, slash, non-ASCII) causes a bail.
Solutions
- Restrict the name to letters, digits, '_' and '-' (e.g. "my_provider-2").
- Validate the pattern in the UI before calling the API.
Example fix
// before persist_custom_provider(path, "My Provider.com", &url, ...)?; // after persist_custom_provider(path, "My_Provider", &url, ...)?;
Defensive patterns
Strategy: validation
Validate before calling
let ok = name.trim().chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-'));
if !ok { return Err(anyhow::anyhow!("invalid provider name characters")); } Type guard
fn is_safe_name(name: &str) -> bool { let v = name.trim(); !v.is_empty() && v.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-')) } Try / catch
match persist_custom_provider(path, name, &url, ...) {
Err(e) if e.to_string().contains("letters, numbers") => sanitize_and_retry(name),
other => other,
} Prevention
- Constrain name inputs to [A-Za-z0-9_-] in the UI.
- Sanitize/slugify user-provided names before persisting.
When it happens
Trigger: Calling persist_custom_provider with a name containing e.g. a space, dot, or non-ASCII character.
Common situations: Users typing "My Provider" or "my.provider" into a custom-provider form.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- CRON field contains an empty list item
- custom provider base URL is invalid
- custom provider base URL is required
- custom provider base URL must be an http(s) URL with a host
- custom provider id cannot be empty
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/e37df22b8595e1e5.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/config_persistence.rs:702
fn normalize_custom_provider_id(raw: &str) -> anyhow::Result<String> {
use anyhow::bail;
let value = raw.trim();
if value.is_empty() {
bail!("custom provider name is required");
}
if value == "__custom__" {
bail!("custom provider name is reserved");
}
if crate::config::ApiProvider::parse(value).is_some() {
bail!("custom provider name must not shadow a built-in provider");
}
if !value
.chars()
.all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '_' | '-'))
{
bail!("custom provider name may only use letters, numbers, '-' and '_'");
}
Ok(value.to_string())
}
fn normalize_custom_provider_base_url(raw: &str) -> anyhow::Result<String> {
use anyhow::bail;
let value = raw.trim().trim_end_matches('/');
if value.is_empty() {
bail!("custom provider base URL is required");
}
let parsed = reqwest::Url::parse(value)
.map_err(|err| anyhow::anyhow!("custom provider base URL is invalid: {err}"))?;
if !matches!(parsed.scheme(), "http" | "https") || parsed.host_str().is_none() {
bail!("custom provider base URL must be an http(s) URL with a host");
}
Ok(value.to_string())
}View on GitHub (pinned to 73e0f67d83)