Troubleshooting

Each section starts with a message that SkillCatalog prints, such as Error: dirty-tree, explains why it appears, and gives the fix. The sections are grouped by what you were doing when the message appeared, in the order most people meet them. Messages show your home folder as ~ and Acme's names in place of yours. Each error's exit code is in the reference.

Catalogs and skills

A new catalog stays empty on the Git host

Warning: SkillCatalog committed the new catalog's starter files, but could not push them to the Git host, so the repository is still empty there. Check that you can push to it, then run `skc sync acme-skills`.

You added an empty repository, and SkillCatalog created the catalog's starter files and committed them in your clone, but the Git host refused the push. The usual cause is access: the account or SSH key that Git uses on this machine cannot push to that repository. The catalog works on your machine, but anyone who adds the repository now gets a separate, empty catalog of their own, so push before you share it. In the desktop app, the same warning appears after Add catalog.

Check that git push works for that repository from a terminal, then sync the catalog, which pushes the commit:

skc sync acme-skills

Adding a catalog reports a missing catalog.yaml

Error: catalog-clone-unreadable
Failed to scaffold catalog: Repository 'Platform Skills' is not a SkillCatalog catalog yet: it has commits but no catalog.yaml at its root or in .skillcatalog/catalog/ (missing required catalog path: expected 'catalog.yaml' or '.skillcatalog/catalog/catalog.yaml'). Commit a catalog.yaml with `id` and `name` and push it.
Hint: SkillCatalog did not add the catalog. Fix the problem named above, then rerun `skc catalog add <source>`.

The repository has commits, such as a README that your Git host created, but no catalog at either supported location. SkillCatalog writes the catalog files itself only into an empty repository, so here you add them. Choose the repository root or .skillcatalog/catalog/, following the location rule. The example below uses the repository root; for a subfolder catalog, create both catalog.yaml and skills/ under .skillcatalog/catalog/ and stage those prefixed paths. Keep a .gitkeep in skills/, because Git does not keep empty folders and skc validate needs the folder:

git clone git@github.com:acme/platform-skills.git
cd platform-skills
mkdir skills
touch skills/.gitkeep

Create catalog.yaml with the catalog id and name you want:

id: platform-skills
name: Platform Skills

Commit and push, then add the catalog again:

git add catalog.yaml skills
git commit -m "Add catalog files"
git push
skc catalog add git@github.com:acme/platform-skills.git

Clones time out

Error: catalog-clone-failed
Clone failed: git operation timed out: clone after 120s
...

SkillCatalog stops a clone, fetch, or push after two minutes, and skc sync reports the same problem as The operation timed out. Check your network and try again. A large repository on a slow connection can need longer, so raise the limit for one command, in seconds:

GIT_TIMEOUT_NETWORK=600 skc catalog add git@github.com:acme/platform-skills.git

A skill is missing from the list

Warning: skill 'review-tests' in catalog 'acme-skills' is not listed because it cannot be read: invalid YAML in frontmatter: missing field `created_at` (~/.skillcatalog/catalogs/acme-skills/skills/review-tests.md)
2 skills in catalog 'acme-skills'.
...

skc skill list lists the skills it can read and prints this warning on standard error for each one it cannot. Here a skill kept in one file has no dates. Fix what the warning names, or move the skill into its own folder as Skill files describes, then sync. Until then, skc validate fails on the file, while delivery of the other skills goes on. In the desktop app, the Skills page shows a row that starts with Cannot read skill "review-tests".

A catalog cannot be removed

Error: catalog-remove-in-use
catalog 'platform-skills' is used by 2 installed profile(s):
- profile 'home' at ~: skill 'incident-response'
- profile 'payments-api' at ~/code/payments-api/.skillcatalog/skillcatalog.yml: the team manifest declares this catalog
Hint: Remove these entries, then rerun `skc catalog remove platform-skills`:
  skc profile remove home --kind skill --slug incident-response --catalog-id platform-skills
...

Each profile listed still uses the catalog, through its own entries, its local manifest, or its team manifest. Run each skc profile remove command that the hint lists. Where a team manifest declares the catalog, run skc uninstall in that checkout, because excluding the manifest's entries does not clear the check. Then remove the catalog:

skc profile remove home --kind skill --slug incident-response --catalog-id platform-skills
cd ~/code/payments-api
skc uninstall
skc catalog remove platform-skills

The hint also offers --force, which removes the catalog anyway. The profiles with entries from it are then not delivered until you remove those entries, so skc deliver and skc sync fail. A checkout whose team manifest declares the catalog adds it again at its next skc install or skc update.

Syncing

Git identity is missing

Error: missing-config
Git is missing required config: user.email and user.name.
Hint: Set `git config --global user.email <you@example.com>` and `git config --global user.name <Your Name>`, then rerun `skc sync`.

SkillCatalog commits under your Git identity, and Git has none. Adding an empty repository as a catalog stops the same way, with missing-git-identity, because SkillCatalog commits the catalog's starter files. Set your name and email, then run the command again:

git config --global user.name "<your name>"
git config --global user.email "<your email>"

Sync refuses to run

Error: dirty-tree
Pending changes detected and stdin is not a terminal; refusing to sync without an explicit commit message.
Catalog Acme Skills (acme-skills) [direct]
  modified: skills/review-checklist/SKILL.md
Hint: Rerun with `--message <msg>` to commit, or run the command in an interactive terminal.

A catalog has changes to commit, and skc sync asks for a commit message only in a terminal. Pass the message:

skc sync --message "Check that migrations can be rolled back"

Another sync is running

Error: another-sync-running
Sync could not start: another operation holds the git index lock. Busy catalog: Acme Skills (acme-skills).
Hint: Wait for the other sync to finish and retry, or remove a stale `.git/index.lock` if you know no other sync is running.

Another sync, a desktop save, or a Git command is using the clone of the busy catalog. Wait, then run the command again. If nothing else is running, a Git command that stopped part way left its lock file in that clone, so delete it:

rm ~/.skillcatalog/catalogs/acme-skills/.git/index.lock

A push is rejected or a pull conflicts

Error: sync-all-failed
Every requested catalog-sync attempt failed.

Catalog results:
- Acme Skills (acme-skills): diverged (Conflicts detected: 1 file could not be merged automatically: skills/review-checklist/SKILL.md.)
...

You and a teammate changed the same lines of the files listed. Merge in the clone with git pull --no-rebase, keep the lines you want, delete the conflict markers, and sync again.

When the Git host refused the push, the catalog's line gives the host's reason instead:

Error: sync-all-failed
Every requested catalog-sync attempt failed.

Catalog results:
- Acme Skills (acme-skills): push failed (The Git host refused the push: Changes to main must go through a pull request. (pre-receive hook declined).)
...

Your commit stays in your clone. Do what the host asks, then sync again. To send changes through review instead, switch the catalog to review branches.

A commit stops on leftover conflict markers

Error: sync-all-failed
Every requested catalog-sync attempt failed.

Catalog results:
- Acme Skills (acme-skills): error (Save blocked by catalog validation
...
- [validation.conflict-markers] catalog '~/.skillcatalog/catalogs/acme-skills' skill 'review-checklist' validation: validation.conflict-markers (line 10-14): leftover Git conflict markers; resolve the conflict and delete the marker lines
...

A merge left in a SKILL.md the <<<<<<<, =======, and >>>>>>> lines that Git adds around a conflict. Before each commit that skc sync or a desktop save makes, the clone's pre-commit hook checks the catalog, and a marker block fails the check. Keep the text you want, delete the marker lines, and sync again.

The check reads only SKILL.md files, and it skips fenced code blocks, where examples of markers belong. After each merge, search the whole clone for the markers it misses:

git -C ~/.skillcatalog/catalogs/acme-skills grep -n '^<<<<<<<'

To turn the check off for one catalog, see The pre-commit check.

A desktop save stops on unsynced changes

Not saved: skills/review-security/SKILL.md has changes that are not synced yet. Sync them first, then save again.

On a direct catalog, a desktop save commits and pushes only the files of the item you saved. Here the clone also holds an unsynced change to that same item, such as a hand edit, so saving would publish that change with yours. The save stops and changes nothing. Sync the change, from the desktop app's Sync Center or in a terminal, then save again:

skc sync acme-skills --message "Check for leaked tokens"

A save also stops when the item uses a skill, stack, category, owner, or tag that exists only in unsynced changes, because the Git host would receive an item that points at something it lacks:

Stack 'code-review' uses skill 'review-tests', which is not synced yet. Sync first. Your change is kept in the catalog folder; Sync publishes it together with every other pending change in this catalog.

Deleting an item stops the same way, with could break references in changes that are not synced yet, when an unsynced file, such as a stack, can refer to it. In both cases your edit stays in the clone, and your next sync publishes it with the other changes.

Sync or update reports an unexpected error

Error: catalog-refresh-failed
catalog 'acme-skills' could not be refreshed: An unexpected error occurred. Please try again.
...

SkillCatalog prints this when Git fails for a reason it does not recognize, such as a lost network connection, and skc sync shows the same text for each catalog. The log keeps Git's own message:

grep git_stderr ~/.skillcatalog/logs/latest.log

Until you fix the cause, skc deliver still delivers from the clones on your machine.

Review branches and proposals

Sync refuses one catalog

Error: sync-all-failed
Every requested catalog-sync attempt failed.

Catalog results:
- Acme Skills (acme-skills): error (Not synced: your change to skills/review-security/SKILL.md overlaps proposal 01m37fg6fptp6cjc6rvvyc44w2 (branch skillcatalog/acme-skills/01m37fg6fptp6cjc6rvvyc44w2-dirty-sync), which changed the same lines. Your changes are back in the catalog folder, uncommitted. Wait until the proposal is merged, then sync again. Editing the item in the desktop app changes the waiting proposal directly. To sync your other catalogs, run `skc sync <catalog-id>`.)
- Platform Skills (platform-skills): dirty (Uncommitted changes found.)
...

A change in acme-skills overlaps a waiting proposal, and while sync refuses one catalog, a sync of every catalog pushes none of them. The desktop app's Sync stops the same way. Until the proposal is merged, sync each of the other catalogs by name in a terminal, and give a commit message if it asks:

skc sync platform-skills

Sync stops on an unfinished merge

Error: sync-all-failed
Every requested catalog-sync attempt failed.

Catalog results:
- Acme Skills (acme-skills): error (Not synced: the catalog folder has an unfinished merge, rebase, or cherry-pick. Finish or abort it first, then sync again.)
...

On a review-branch catalog, sync stops while the clone has an unfinished merge, rebase, or cherry-pick. Finish it with Git or abort it, then sync again. To abort a merge:

git -C ~/.skillcatalog/catalogs/acme-skills merge --abort
skc sync --message "Log every export"

For a rebase or a cherry-pick, run git rebase --abort or git cherry-pick --abort instead.

Local commits conflict with the Git host

Error: sync-all-failed
Every requested catalog-sync attempt failed.

Catalog results:
- Acme Skills (acme-skills): error (Conflicts detected: 1 file could not be merged automatically: skills/review-checklist/SKILL.md. Local commits on main conflict with origin/main; SkillCatalog did not publish or deliver them. To turn only your local changes into a proposal, run `git -C '~/.skillcatalog/catalogs/acme-skills' reset --soft 8d92b6fc4fafea9f7c1fa22ba43383b42774879c`, then `skc sync --message "<message>"`. To sync your other catalogs, run `skc sync <catalog-id>`.)
...

On a review-branch catalog, sync turns commits on main that the Git host lacks into a proposal. It pulls first, and here the host's main changed the same lines. The message names the commit where your work split from the host's. Reset to that commit, which turns your commits back into uncommitted changes, then sync them as a proposal:

git -C ~/.skillcatalog/catalogs/acme-skills reset --soft <commit>
skc sync --message "Tests pass locally"

Recover unpublished changes

Warning: Acme Skills (acme-skills) has 1 recovery file(s) from syncs that did not finish, in ~/.skillcatalog/proposals/acme-skills/recovery. See "Recover unpublished changes" in the troubleshooting guide.

On a review-branch catalog, sync takes your edits out of the clone while it sends them as a proposal, and puts them back when the push fails. When it cannot put a file back, for example because the file changed while the sync ran, SkillCatalog does not overwrite it. It keeps your version in a recovery file instead, and the failed sync names that file. Until the folder has no recovery files, each later skc sync prints this warning on standard error, unless you pass --json. The desktop app does not show it.

Each recovery file holds the edits of one sync, under payloads. Each entry has the file's path in the catalog, its bytes as a list of numbers in contents, and deleted when the edit deleted the file. To write the files of one recovery file into ~/recovered, run this in the folder the warning names, with that file's name:

cd ~/.skillcatalog/proposals/acme-skills/recovery
python3 -c 'import json, pathlib, sys
for p in json.load(open(sys.argv[1]))["payloads"]:
    if not p["deleted"]:
        out = pathlib.Path.home() / "recovered" / p["path"]
        out.parent.mkdir(parents=True, exist_ok=True)
        out.write_bytes(bytes(p["contents"]))' 3c5980b992d9a7d942e866e72e169897.json

Compare each file with the clone's copy, copy the changes you want into the clone, and sync. Then delete the recovery file:

rm ~/.skillcatalog/proposals/acme-skills/recovery/3c5980b992d9a7d942e866e72e169897.json

Delivery is blocked on a review-branch catalog

Error: delivery-failed
Delivery for all profiles did not complete cleanly: 0 conflicts, 1 failed item.
...
Failures:
- ~/.claude/skills/incident-response (Platform Skills, incident-response, Claude Code) (profile 'home') : delivery blocked: catalog 'platform-skills' has 1 pending proposal(s) in RemoteStatusUnknown state; run `skc proposal refresh` before delivering [reason: blocked_remote_status_unknown; actions: retry]
...

The last proposal check could not reach the Git host, so SkillCatalog does not know whether the proposal was merged. Until it knows, delivery skips that catalog's skills and keeps the copies it delivered earlier, while it still delivers everything else. When no profile can be delivered at all, skc deliver stops with delivery-blocked-by-remote-status-unknown instead. Check the proposals again, then deliver:

skc proposal refresh
skc deliver

A proposal is missing from the list

0 active proposals.

skc proposal list does not show a proposal that your last sync reported as push proposal. SkillCatalog compares file contents, not file modes, so a proposal that only changes whether a file is executable matches main. SkillCatalog then counts it as merged the next time it checks proposals, and a sync checks right after it sends one.

The proposal's branch stays on the Git host, and skc proposal list --include-remote-only still lists it. Review and merge its pull request as usual.

Delivery

A skill folder already exists

Added skill 'release-notes' from catalog 'acme-skills' to profile 'home'.

Warning: delivery after mutation failed.
delivery completed with 1 failed item; rerun delivery to inspect and repair

skc deliver gives the reason:

Error: delivery-failed
Delivery for all profiles did not complete cleanly: 0 conflicts, 1 failed item.
...
- ~/.claude/skills/release-notes/SKILL.md (Acme Skills, Release notes, Claude Code) : target file exists in the app folder but is not managed by SkillCatalog [reason: unmanaged_target_exists; actions: inspect, skip, replace]
...

SkillCatalog did not write this file, for example because you made the skill by hand, so delivery leaves it in place. Move your folder away, then deliver:

mv ~/.claude/skills/release-notes ~/release-notes.bak
skc deliver

A file that is exactly what delivery would write gets the same treatment. Its line says target file matches the catalog version but is not managed by SkillCatalog, and the output names the command that takes such files over. This happens when you copy a delivered skill by hand, or when a profile's delivery record is lost. Take the files over for one profile:

skc deliver home --adopt-matching

--adopt-matching takes over only files that match exactly, and never overwrites a file with different content. Without a profile id, it works on every profile.

In the desktop app, the Sync Center lists these files on a card. Replace and deliver writes the catalog's version over files with different content and takes over files that match, so SkillCatalog then manages all of them. To share your own version instead, bring it into a catalog.

A delivered file is reported as changed

Error: delivery-check-drift
Drift check for all profiles: drift detected, 1 manually edited, 0 deleted, 0 unreadable.

Manually edited:
- ~/.claude/skills/review-checklist/SKILL.md [claude-code] Review checklist
...

Someone edited a delivered copy. Delivery keeps the edit and lists the file as preserved, so your tools read the edited copy.

To keep the edit for everyone, copy the changed lines into the catalog's copy of the skill. Then delete the delivered copy, deliver, and sync:

rm ~/.claude/skills/review-checklist/SKILL.md
skc deliver
skc sync --message "Check endpoint examples"

Turning a tool off keeps an edited file

Disabled delivery target 'cursor'.
Warning: delivery after settings change failed.
delivery completed with 1 preserved edited file; rerun delivery to inspect and repair

Turning a tool off removes the skills SkillCatalog delivered to it, but it never deletes a file you edited, so that file stays in the tool's skills folder. Delete it yourself, then deliver, so that SkillCatalog stops tracking it:

skc deliver
Error: delivery-failed
Delivery for profile 'home' did not complete cleanly: 0 conflicts, 1 failed item.
...
- ~/.claude/skills/review-security (Acme Skills, review-security, Claude Code) (profile 'home') : could not locate source payload for skill 'review-security' under catalog skill layout: source skill companion '~/.skillcatalog/catalogs/acme-skills/skills/review-security/scripts/find-secrets.sh' must not be a symlink [reason: symlink_in_skill; actions: inspect]
...

Delivery never copies a symbolic link, so it skips the skill that holds one, keeps the copy it delivered earlier, and delivers the profile's other skills. skc validate reports the link too, and until the link is gone, the check before each commit refuses every commit to the catalog. Replace the link with a copy of its target, here ~/tools/find-secrets.sh, then sync:

cd ~/.skillcatalog/catalogs/acme-skills/skills/review-security/scripts
rm find-secrets.sh
cp ~/tools/find-secrets.sh find-secrets.sh
skc sync --message "Copy the secrets script into the skill"

Two entries deliver the same skill

Error: delivery-failed
Delivery for all profiles did not complete cleanly: 1 conflict, 0 failed items.
...
  CONFLICT: ~/.claude/skills/review-checklist/SKILL.md (Review checklist, Claude Code) - picked skill review-checklist (Acme Skills), dropped skill review-checklist (Platform Skills)
...

Two entries in one profile deliver a skill with the same slug from different catalogs. Until you remove one of them, skc deliver and skc sync fail, and skc status and skc validate report the conflict:

skc profile remove home --catalog-id platform-skills --kind skill --slug review-checklist

A profile entry is missing from the catalog

Error: delivery-failed
Catalogs synced, but delivery reported 0 conflicts, 1 failed item.
...
Failures:
- profile 'home' was not delivered: profile 'home' entry 'skill:review-security' with catalog id 'acme-skills' cannot resolve delivery content safely against catalog 'Acme Skills' (git@github.com:acme/acme-skills.git): profile entry 'skill:review-security' is missing from the catalog [reason: broken_entry; actions: inspect]
...

The profile lists a skill that its catalog does not have, here because a teammate deleted review-security. skc profile add refuses a slug that the catalog lacks, so this happens when the catalog changes after you add the entry, or when someone edits the profile or a manifest by hand and misspells a slug.

Delivery skips the whole profile and keeps the copies it delivered earlier, while it still delivers your other profiles. skc deliver and skc sync fail until you remove the entry:

skc profile remove home --catalog-id acme-skills --kind skill --slug review-security

For an entry in a team manifest, fix the manifest and commit it instead.

Project profiles

Install or update cannot find the team manifest

Error: manifest-missing
no team manifest exists at '~/code/payments-api/src/.skillcatalog/skillcatalog.yml'
Hint: Commit `.skillcatalog/skillcatalog.yml` in this checkout, then rerun `skc update`.

You ran the command in a subfolder, and skc install and skc update read the team manifest only in the current folder. Run it at the top of the checkout:

cd "$(git rev-parse --show-toplevel)"
skc update

Install stops with a catalog mismatch

Error: install-url-mismatch
manifest '~/code/payments-api/.skillcatalog/skillcatalog.yml' declares catalog id 'acme-skills' with source 'git@github.com:acme/acme-skills.git' but the registered source is 'https://github.com/acme/acme-skills.git'
...

You added the catalog with a different URL than the team manifest's, here HTTPS instead of SSH, and SkillCatalog compares URLs as text. Add the catalog again with the manifest's URL. Removing it deletes its clone with any changes you have not synced, so sync it first:

skc sync acme-skills

Only after it prints Synced 1 catalog, remove the catalog while keeping the entries that use it. skc install then adds the catalog again from the manifest's URL:

skc catalog remove acme-skills --force
skc install

A wrong catalog id in the team manifest stops install too:

Error: install-intrinsic-id-mismatch
manifest '~/code/payments-api/.skillcatalog/skillcatalog.yml' declares catalog id 'acme' but source 'git@github.com:acme/acme-skills.git' declares catalog id 'acme-skills'
...

In the team manifest, change the key under catalogs: and each entry's catalog_id to acme-skills, the id the catalog declares, then commit.

A profile's folder cannot be found

Error: delivery-failed
Catalogs synced, but delivery reported 0 conflicts, 1 failed item.
...
Failures:
- ~/code/payments-api-hotfix (profile 'payments-api') : profile 'payments-api': folder '~/code/payments-api-hotfix' cannot be found (moved, deleted, or on a disk that is not mounted); mount it and rerun, or run `skc install --target-dir .` in '~/code/payments-api' to deliver there again [reason: checkout_missing; actions: inspect]
...

A project profile delivers into its checkout, or into the folder you saved with skc install --target-dir. Here that folder is gone, so delivery skips the profile and delivers the others. If the folder is on a disk that is not mounted, mount it and deliver again. For a worktree you deleted, deliver into the checkout again:

cd ~/code/payments-api
skc install --target-dir .

If you moved or deleted the checkout itself, remove its profile with skc profile delete payments-api, then run skc install in the checkout's new place.

git pull stops on .gitignore

error: Your local changes to the following files would be overwritten by merge:
	.gitignore

The first time you make a personal change in a checkout, SkillCatalog adds two lines to its .gitignore, which leaves that file modified. When a teammate commits the same lines, git pull refuses to overwrite your copy. First check what your copy changes:

git diff .gitignore

If the diff shows only SkillCatalog's two lines, discard your copy and pull:

git checkout -- .gitignore
git pull

If it also shows lines of your own, commit or stash them before you pull. SkillCatalog expects its two lines to be the last rules in the file, so keep them at the end whenever you commit .gitignore.

shared source changed after this install link was resolved; reopen the share link and try again

When you choose Install, SkillCatalog reads the Git host again, and it stops when the catalog changed after you opened the link, for example because a teammate pushed. Nothing is installed. Open the link again, which reads the new version.

registered shared catalog has local changes; sync or repair it before installing this link

You already have this catalog, and its clone holds local changes. This includes host files outside a subfolder catalog. SkillCatalog updates the whole clone before it installs, so it stops and changes nothing. When the changes are new files, the message says untracked local changes instead. Sync catalog edits, then open the link again:

skc sync acme-skills --message "Add the rollback checklist"

If the message comes back, use git -C ~/.skillcatalog/catalogs/acme-skills status to find the remaining changes. Commit or stash host-file changes with Git, or move new files out of the clone, before retrying. See which files sync commits.

Logs and reporting a problem

Find and share logs

The log has details that messages leave out. The newest log file is ~/.skillcatalog/logs/latest.log. To record more detail, run the failing command again with SKILLCATALOG_LOG=debug:

SKILLCATALOG_LOG=debug skc sync

Log files are not redacted. Skill and catalog names appear in them as short codes, such as rev~a1deb48cb5, that differ between installations, but catalog ids, file paths, repository URLs, and Git's error output appear in full. Read a log and remove anything private before you share it.

In the desktop app, choose Logs in the sidebar, pick a file, then choose Save to Downloads or Copy log package. The package holds the end of that file, with tokens, email addresses, and most paths replaced by markers. A space in a folder or file name can still leave part of a path, so read the package before you share it.

Report a problem

Email hi@skillcatalog.dev with:

  • The output of skc --version.
  • The command you ran and its full output. For skc, add the output of the same command with --json.
  • What you expected to happen.
  • The log lines from that run, after you check them.