Hmbown/CodeWhale · error
invalid registry url
Error message
invalid registry url: {registry_url} What it means
`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.
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
Example fix
// before fetch_registry(&network, "my-registry.example.com").await?; // after fetch_registry(&network, "https://my-registry.example.com/skills.json").await?;
Defensive patterns
Strategy: validation
Validate before calling
fn valid_registry_url(u: &str) -> bool {
url::Url::parse(u).map(|p| p.host_str().is_some() && matches!(p.scheme(), "http" | "https")).unwrap_or(false)
} Prevention
- Always store registry URLs with scheme and host in config
- Trim user-supplied URLs
- Validate config URLs at startup
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- --base-url must use http or https
- browser URL cannot be empty
- Codewhale account API base URL must be an origin without a…
- Codewhale account API base URL must not contain a query or…
- returned an unusable verification URI
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/055d5a846fccb300.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/skills/install.rs:550
if !target.join(INSTALLED_FROM_MARKER).exists() {
return Err(InstallError::NotInstalledHere(name.to_string()).into());
}
let content_digest = super::package_digest::compute_package_digest(&target)
.with_context(|| format!("cannot compute content digest for {}", target.display()))?;
write_trust_v2(&target, &content_digest)?;
Ok(())
}
/// Fetch the curated registry and return the parsed entries.
///
/// Honours `network` (skipping the call entirely on Deny / Prompt).
pub async fn fetch_registry(
network: &NetworkPolicy,
registry_url: &str,
) -> Result<RegistryFetchResult> {
let host = match host_from_url(registry_url) {
Some(host) => host,
None => bail!("invalid registry url: {registry_url}"),
};
match network.decide(&host) {
Decision::Allow => {}
Decision::Deny => return Ok(RegistryFetchResult::Denied(host)),
Decision::Prompt => return Ok(RegistryFetchResult::NeedsApproval(host)),
}
let body = reqwest_client()
.get(registry_url)
.send()
.await
.with_context(|| format!("failed to fetch registry {registry_url}"))?
.error_for_status()
.with_context(|| format!("registry {registry_url} returned an error status"))?
.text()
.await
.with_context(|| format!("failed to read registry body from {registry_url}"))?;
let parsed: RegistryDocument = serde_json::from_str(&body)
.with_context(|| format!("failed to parse registry json from {registry_url}"))?;View on GitHub (pinned to 73e0f67d83)