{"record":{"id":"055d5a846fccb300","repo":"Hmbown/CodeWhale","slug":"invalid-registry-url-registry-url","errorCode":null,"errorMessage":"invalid registry url: {registry_url}","messagePattern":"invalid registry url: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/skills/install.rs","lineNumber":550,"sourceCode":"    if !target.join(INSTALLED_FROM_MARKER).exists() {\n        return Err(InstallError::NotInstalledHere(name.to_string()).into());\n    }\n    let content_digest = super::package_digest::compute_package_digest(&target)\n        .with_context(|| format!(\"cannot compute content digest for {}\", target.display()))?;\n    write_trust_v2(&target, &content_digest)?;\n    Ok(())\n}\n\n/// Fetch the curated registry and return the parsed entries.\n///\n/// Honours `network` (skipping the call entirely on Deny / Prompt).\npub async fn fetch_registry(\n    network: &NetworkPolicy,\n    registry_url: &str,\n) -> Result<RegistryFetchResult> {\n    let host = match host_from_url(registry_url) {\n        Some(host) => host,\n        None => bail!(\"invalid registry url: {registry_url}\"),\n    };\n    match network.decide(&host) {\n        Decision::Allow => {}\n        Decision::Deny => return Ok(RegistryFetchResult::Denied(host)),\n        Decision::Prompt => return Ok(RegistryFetchResult::NeedsApproval(host)),\n    }\n    let body = reqwest_client()\n        .get(registry_url)\n        .send()\n        .await\n        .with_context(|| format!(\"failed to fetch registry {registry_url}\"))?\n        .error_for_status()\n        .with_context(|| format!(\"registry {registry_url} returned an error status\"))?\n        .text()\n        .await\n        .with_context(|| format!(\"failed to read registry body from {registry_url}\"))?;\n    let parsed: RegistryDocument = serde_json::from_str(&body)\n        .with_context(|| format!(\"failed to parse registry json from {registry_url}\"))?;","sourceCodeStart":532,"sourceCodeEnd":568,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/skills/install.rs#L532-L568","documentation":"`fetch_registry` parses the registry URL to extract a host for the network-policy decision. If `host_from_url` returns `None` the string is not a usable absolute URL and no policy decision can be made, so it fails fast with an invalid-URL error before any network activity.","triggerScenarios":"Calling `fetch_registry(network, registry_url)` (directly or via `sync_registry`/`candidate_urls`) with a string like `\"my-registry\"`, `\"ftp://x\"`-style unsupported schemes, empty string, or a URL missing a scheme/host.","commonSituations":"Config file with a bare hostname instead of `https://host/registry.json`; environment variable left empty; typos like `https:/host` (single slash); copy-paste dropping the scheme.","solutions":["Use a full absolute URL with scheme, e.g. `https://registry.example.com/skills.json`","Check the registry URL config/env value for truncation or missing scheme","Trim whitespace before passing"],"exampleFix":"// before\nfetch_registry(&network, \"my-registry.example.com\").await?;\n// after\nfetch_registry(&network, \"https://my-registry.example.com/skills.json\").await?;","handlingStrategy":"validation","validationCode":"fn valid_registry_url(u: &str) -> bool {\n    url::Url::parse(u).map(|p| p.host_str().is_some() && matches!(p.scheme(), \"http\" | \"https\")).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always store registry URLs with scheme and host in config","Trim user-supplied URLs","Validate config URLs at startup"],"tags":["registry","url","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}