Hmbown/CodeWhale · error
skill '{name}' was imported locally (spec '{}') and cannot b
Error message
skill '{name}' was imported locally (spec '{}') and cannot be updated from a registry; re-import or remove it first What it means
The skill update path refuses to update a skill whose .installed-from marker records a non-registry-updatable spec (a local import or direct source). Update is registry-driven by contract, so a locally imported skill must be re-imported or removed first. The marker's actual spec is echoed in the message so the origin is visible.
Source
Thrown at crates/tui/src/skills/install.rs:446
skills_dir: &Path,
max_size: u64,
network: &NetworkPolicy,
registry_url: &str,
) -> Result<UpdateResult> {
let target = skill_target_path(name, skills_dir)?;
if target.exists() {
ensure_target_within_skills_dir(&target, skills_dir)?;
}
let marker_path = target.join(INSTALLED_FROM_MARKER);
if !marker_path.exists() {
return Err(InstallError::NotInstalledHere(name.to_string()).into());
}
let marker_body = fs::read_to_string(&marker_path)
.with_context(|| format!("failed to read {}", marker_path.display()))?;
let marker: InstalledFromMarker = serde_json::from_str(&marker_body)
.with_context(|| format!("malformed {INSTALLED_FROM_MARKER} for {name}"))?;
if !is_registry_updatable_spec(&marker.spec) {
bail!(
"skill '{name}' was imported locally (spec '{}') and cannot be updated from a registry; \
re-import or remove it first",
marker.spec
);
}
// Re-resolve the URL, taking the existing checksum as a short-circuit hint:
// we still hit the network so the user gets a useful "no upstream change"
// signal, but we skip the unpack step if the bytes match.
let source = InstallSource::parse(&marker.spec)?;
let urls = match candidate_urls(&source, network, registry_url).await? {
UrlResolution::Resolved(urls) => urls,
UrlResolution::NeedsApproval(host) => return Ok(UpdateResult::NeedsApproval(host)),
UrlResolution::Denied(host) => return Ok(UpdateResult::NetworkDenied(host)),
};
let (bytes, _url) = match download_first_success(&urls, network, max_size).await? {
DownloadOutcome::Bytes { bytes, url } => (bytes, url),
DownloadOutcome::NeedsApproval(host) => return Ok(UpdateResult::NeedsApproval(host)),View on GitHub (pinned to 0c42157ee5)
Solutions
- Remove the skill ('/skill uninstall <name>') and install it fresh from the registry spec.
- Or re-import it deliberately from the local source to pick up the new content.
- Inspect the .installed-from file under the skill directory to see the recorded spec before deciding.
- Do not hand-edit the marker to force updates; the spec drives checksum verification.
Example fix
# before /skill update my-skill # -> was imported locally (spec 'file:///opt/my-skill') # after /skill uninstall my-skill /skill install my-skill
Defensive patterns
Strategy: fallback
Validate before calling
// Before updating, read the marker and check the spec origin
let marker: InstalledFromMarker =
serde_json::from_str(&std::fs::read_to_string(dir.join(".installed-from"))?)?;
let registry_updatable = !marker.spec.starts_with("file:"); // mirror is_registry_updatable_spec
if !registry_updatable {
// choose re-import or uninstall+install instead of update
} Try / catch
match skills::install::update(&name, &skills_dir, &network, ®istry_url).await {
Err(err) if err.to_string().contains("cannot be updated from a registry") => {
// fallback: uninstall then install fresh from the registry
skills::install::uninstall(&name, &skills_dir)?;
skills::install::install(&format!("github:{name}"), &skills_dir, &network).await?;
}
other => other?,
} Prevention
- Prefer registry installs when you plan to use '/skill update' long-term.
- Keep local imports in a separately named skill so blanket updates skip them.
- Check the .installed-from marker before bulk update scripts.
When it happens
Trigger: '/skill update <name>' where the skill was installed via local import or a direct URL rather than from the registry; the is_registry_updatable_spec check on the marker fails and this bails before any network call.
Common situations: Teams where some skills came from a local checkout and someone later runs a blanket update; migrating from URL-based installs to the registry; markers written by older versions with non-registry specs.
Related errors
- invalid registry url: {registry_url}
- registry source '{name}' cannot be fetched as a plain tarbal
- registry entry for '{name}' must not point to another regist
- install source must not be empty
- github source must be 'github:owner/repo' (got {spec})
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/695b31db94b21f19.
Report an issue: GitHub.