Hmbown/CodeWhale · error
registry source '{name}' cannot be fetched as a plain tarbal
Error message
registry source '{name}' cannot be fetched as a plain tarball What it means
fetch_tarball maps GitHubRepo and DirectUrl sources to concrete tarball URLs, but a Registry source has no tarball to fetch; registry entries must be resolved through candidate_urls, which consults the registry index for the real source. Calling fetch_tarball on a Registry variant is an internal API misuse, not a user input error.
Source
Thrown at crates/tui/src/skills/install.rs:1023
}
/// Resolve a *remote* [`InstallSource`] (GitHub repo or direct tarball URL)
/// and download the first reachable candidate under the network policy.
/// Registry sources are rejected: skill registry resolution stays inside
/// [`candidate_urls`], and the plugin install on-ramp has no registry index.
pub(crate) async fn fetch_tarball(
source: &InstallSource,
network: &NetworkPolicy,
max_size: u64,
) -> Result<FetchOutcome> {
let urls = match source {
InstallSource::GitHubRepo(repo) => vec![
format!("https://github.com/{repo}/archive/refs/heads/main.tar.gz"),
format!("https://github.com/{repo}/archive/refs/heads/master.tar.gz"),
],
InstallSource::DirectUrl(url) => vec![url.clone()],
InstallSource::Registry(name) => {
bail!("registry source '{name}' cannot be fetched as a plain tarball")
}
};
Ok(
match download_first_success(&urls, network, max_size).await? {
DownloadOutcome::Bytes { bytes, url } => FetchOutcome::Bytes { bytes, url },
DownloadOutcome::NeedsApproval(host) => FetchOutcome::NeedsApproval(host),
DownloadOutcome::Denied(host) => FetchOutcome::Denied(host),
},
)
}
/// Resolve the source spec into one or more candidate URLs to try in order.
async fn candidate_urls(
source: &InstallSource,
network: &NetworkPolicy,
registry_url: &str,
) -> Result<UrlResolution> {
match source {View on GitHub (pinned to 0c42157ee5)
Solutions
- Resolve first: call candidate_urls(&source, ...) and fetch the resulting GitHub/DirectUrl source.
- Keep the resolve-then-fetch ordering in one place so future call sites cannot skip it.
- If plain-tarball fetches for registry entries are genuinely needed, teach the registry format to carry tarball URLs explicitly instead of special-casing the client.
Example fix
// before let source = InstallSource::Registry(name.clone()); let outcome = fetch_tarball(&source, &network, MAX).await?; // after let urls = candidate_urls(&source, &network, registry_url).await?; // resolves to GitHub/DirectUrl let outcome = fetch_tarball(&resolved_source, &network, MAX).await?;
Defensive patterns
Strategy: validation
Validate before calling
match &source {
InstallSource::Registry(_) => {
// resolve through candidate_urls first; fetch_tarball cannot handle this
}
_ => { /* fetch_tarball is safe here */ }
} Type guard
fn is_tarball_fetchable(source: &InstallSource) -> bool {
!matches!(source, InstallSource::Registry(_))
} Prevention
- Keep the resolve-then-fetch ordering in one code path.
- Encode the invariant in the type: resolve to a ResolvedSource before exposing fetch_tarball.
- Cover the Registry branch in fetch_tarball tests so refactors cannot silently drop the guard.
When it happens
Trigger: Code (or a test) constructing InstallSource::Registry(name) and calling fetch_tarball directly instead of resolving through candidate_urls first. The public install flow does not hit this; only a path that bypasses the resolution layer does.
Common situations: New code shortcutting resolution, refactors that lose the resolve-then-fetch ordering, and tests exercising fetch_tarball with fixture sources.
Related errors
- skill '{name}' was imported locally (spec '{}') and cannot b
- invalid registry url: {registry_url}
- registry entry for '{name}' must not point to another regist
- CRON {field_name} field must not be empty
- install source must not be empty
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/21fb57023702fc60.
Report an issue: GitHub.