{"record":{"id":"1d1ddca05c23c918","repo":"Hmbown/CodeWhale","slug":"runtime-provider-identity-is-invalid","errorCode":null,"errorMessage":"Runtime provider identity is invalid","messagePattern":"Runtime provider identity is invalid","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/app-server/src/lib.rs","lineNumber":1657,"sourceCode":"            .get(\"providers\")\n            .and_then(Value::as_array)\n            .and_then(|providers| {\n                providers\n                    .iter()\n                    .find(|provider| provider.get(\"id\").and_then(Value::as_str) == Some(current))\n            })\n            .context(\"Runtime provider is unavailable\")?;\n        let model = requested_model\n            .or_else(|| provider.get(\"default_model\").and_then(Value::as_str))\n            .context(\"maxOutputTokens requires an exact model\")?;\n        if model.trim().is_empty() || model.eq_ignore_ascii_case(\"auto\") {\n            bail!(\"maxOutputTokens requires an exact model\");\n        }\n        if !current\n            .bytes()\n            .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'-')\n        {\n            bail!(\"Runtime provider identity is invalid\");\n        }\n        let mut cursor = None;\n        let mut seen = std::collections::HashSet::new();\n        loop {\n            let mut url =\n                reqwest::Url::parse(&format!(\"{}/v1/providers/{current}/models\", self.base_url))?;\n            url.query_pairs_mut().append_pair(\"limit\", \"250\");\n            if let Some(cursor) = cursor.as_deref() {\n                url.query_pairs_mut().append_pair(\"cursor\", cursor);\n            }\n            let catalog = self.request_json(self.authed(self.client.get(url))).await?;\n            if let Some(entry) =\n                catalog\n                    .get(\"models\")\n                    .and_then(Value::as_array)\n                    .and_then(|models| {\n                        models\n                            .iter()","sourceCodeStart":1639,"sourceCodeEnd":1675,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/app-server/src/lib.rs#L1639-L1675","documentation":"The resolved provider identity used to build the Runtime model-catalog URL is validated to contain only lowercase ASCII letters, digits, and hyphens. If the provider name (taken from the current provider iteration) contains other characters, the app-server rejects it before issuing a request, since it would produce an invalid/unintended provider route.","triggerScenarios":"The `current` provider identifier in the output-token capability scan fails the `ascii_lowercase | digit | '-'` byte check — e.g. contains uppercase, underscores, dots, or slashes.","commonSituations":"A provider configured with a name like `My_Provider` or `openai.com` instead of the kebab-case identifiers the Runtime expects.","solutions":["Rename the provider in the Runtime configuration to kebab-case (lowercase letters, digits, hyphens only).","Check where the provider id originates (config file vs. catalog listing) and correct the source value.","Verify the Runtime version's expected provider naming scheme."],"exampleFix":"// before (provider config)\nprovider = \"OpenAI_Main\"\n// after\nprovider = \"openai-main\"","handlingStrategy":"validation","validationCode":"// validate provider ids are kebab-case before use\nfn valid_provider(id: &str) -> bool {\n    !id.is_empty() && id.bytes().all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-')\n}","typeGuard":"const isKebabProviderId = (s) => typeof s === 'string' && /^[a-z0-9-]+$/.test(s);","tryCatchPattern":null,"preventionTips":["Enforce kebab-case naming for providers in config files and tooling.","Add a config-load check that rejects provider ids outside [a-z0-9-].","Document the naming rule where providers are defined."],"tags":["validation","identifier","configuration","runtime"],"backgroundTag":"invalid-identifier-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T21:17:16.096Z"}