{"record":{"id":"0052f02054be702f","repo":"Hmbown/CodeWhale","slug":"custom-provider-name-is-required","errorCode":null,"errorMessage":"custom provider name is required","messagePattern":"custom provider name is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/config_persistence.rs","lineNumber":690,"sourceCode":"                unset_document_value(doc, &[entry[0], entry[1], \"api_key_env\"])?;\n                if provider_id == \"ds4\" && crate::config::base_url_uses_local_host(&base_url) {\n                    set_document_value(doc, &[entry[0], entry[1], \"auth_mode\"], \"none\")?;\n                } else {\n                    unset_document_value(doc, &[entry[0], entry[1], \"auth_mode\"])?;\n                }\n            }\n        }\n        Ok(())\n    })?;\n    Ok(path)\n}\n\nfn normalize_custom_provider_id(raw: &str) -> anyhow::Result<String> {\n    use anyhow::bail;\n\n    let value = raw.trim();\n    if value.is_empty() {\n        bail!(\"custom provider name is required\");\n    }\n    if value == \"__custom__\" {\n        bail!(\"custom provider name is reserved\");\n    }\n    if crate::config::ApiProvider::parse(value).is_some() {\n        bail!(\"custom provider name must not shadow a built-in provider\");\n    }\n    if !value\n        .chars()\n        .all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '_' | '-'))\n    {\n        bail!(\"custom provider name may only use letters, numbers, '-' and '_'\");\n    }\n    Ok(value.to_string())\n}\n\nfn normalize_custom_provider_base_url(raw: &str) -> anyhow::Result<String> {\n    use anyhow::bail;","sourceCodeStart":672,"sourceCodeEnd":708,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/config_persistence.rs#L672-L708","documentation":"normalize_custom_provider_id rejects an empty or whitespace-only custom provider name; persist_custom_provider calls it before writing the [providers.<name>] table, so a blank name can never create an unnamed provider entry.","triggerScenarios":"Calling persist_custom_provider with an empty or whitespace-only provider name.","commonSituations":"A UI form submitted with the name field blank; a script interpolating an unset variable into the name argument.","solutions":["Supply a non-empty name before calling persist_custom_provider.","Trim and check the name in the caller and show a form-validation error."],"exampleFix":"// before\npersist_custom_provider(path, \"\", &url, ...)?;\n// after\nlet name = name.trim();\nanyhow::ensure!(!name.is_empty(), \"provider name required\");\npersist_custom_provider(path, name, &url, ...)?;","handlingStrategy":"validation","validationCode":"let name = raw.trim();\nif name.is_empty() { return Err(anyhow::anyhow!(\"provider name required\")); }","typeGuard":"fn valid_name(raw: &str) -> Option<&str> { let v = raw.trim(); (!v.is_empty()).then_some(v) }","tryCatchPattern":"match persist_custom_provider(path, name, &url, ...) {\n    Err(e) if e.to_string().contains(\"name is required\") => show_form_error(\"name required\"),\n    other => other,\n}","preventionTips":["Require the name field in forms/UIs.","Trim input before submitting.","Reject empty names at the CLI arg-parsing layer."],"tags":["validation","custom-provider","empty-value"],"backgroundTag":"empty-required-field","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}