pbakaus/impeccable · error
Nothing was linked because matching skill folders already ex
Error message
Nothing was linked because matching skill folders already exist.
What it means
After linking, if zero skills were linked and zero already existed, and some were skipped because matching skill folders already exist, the command fails with this message and exit code 1. The existing skills were deliberately left untouched.
Source
Thrown at crates/skills/src/commands.rs:491
if !yes {
out(io, &format!("Source checkout: {}", source.checkout_root));
out(io, &format!("Target harness folder(s): {}", targets.join(", ")));
let ans = prompt.ask(io, &format!("Link impeccable skills into {} folder(s)? (Y/n) ", targets.len()))?;
if ans == "n" || ans == "no" {
out(io, "Aborted. Re-run with --providers=<names> to choose explicitly (e.g. --providers=claude,cursor).");
return Err(Flow::Exit(0));
}
}
let result = bundle::link_provider_skills(io, &source.bundle_root, &root, &targets, force).map_err(Flow::Throw)?;
// Linked installs are excluded from install/update refreshes (overwriting
// a symlink would destroy the link), so this is the only path that can
// deliver the OpenCode command bridge to them. A copy, not a symlink: the
// bridge is static and OpenCode scans the real commands dir. No-ops when
// the source checkout has no built commands (#483).
bundle::copy_provider_commands(&sys, &source.bundle_root, &root, &targets, Some(Scope::Project));
if result.linked == 0 && result.already == 0 {
if result.skipped > 0 {
err(io, "Nothing was linked because matching skill folders already exist.");
err(io, "Existing skills were left untouched. Re-run with --force to replace them with links.");
} else {
err(io, &format!("Nothing was linked: {} had no variants for {}.", source.bundle_root, targets.join(", ")));
}
return Err(Flow::Exit(1));
}
// Linked skill dirs resolve into the source checkout; repair the
// launcher/binary executable bit there too (through the symlink), so a
// checkout whose copier dropped modes still runs.
for provider in &targets {
let skills_dir = util::jsp::join(&[&root, provider, "skills"]);
for skill in util::read_dir_names(&skills_dir).unwrap_or_default() {
let dir = util::jsp::join(&[&skills_dir, &skill]);
if util::is_dir(&dir) {
crate::engine_binary::ensure_executable_scripts(&dir);
}
}
}View on GitHub (pinned to 2bc2879276)
Solutions
- Re-run with `--force` to replace existing folders with links
- Manually remove the existing skill folders and re-run `link`
Example fix
// before impeccable link --providers=claude // after impeccable link --providers=claude --force
Defensive patterns
Strategy: validation
Validate before calling
const existing = fs.existsSync(path.join(target, 'skills', skillName));
if (existing && !force) console.error('Skill exists; use --force to link over it'); Try / catch
try { await link(); } catch (e) { if (/already exist/.test(String(e))) { console.error('Re-run with --force'); } } Prevention
- Check for existing skill folders before linking
- Decide up front whether you want copies or links and stick with one
- Use --force deliberately after reviewing existing skills
When it happens
Trigger: Running `impeccable link` when every candidate skill name already has a folder in the target harness directory (non-linked copies).
Common situations: Re-running link after a previous copy-based install; users want links but real folders shadow them.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- {e} (dynamic error string from bundle::resolve_link_source)
- Could not determine a target harness folder.
- Pass one explicitly, e.g. --providers=claude,cursor
- Existing skills were left untouched. Re-run with --force to
- Nothing was linked: {} had no variants for {}.
AI-assisted analysis of pbakaus/impeccable@2bc2879276 (2026-09-08).
Data as JSON: /api/errors/1a8c342e960f6a10.
Report an issue: GitHub.