You added a custom skill under ~/.claude/skills/my-skill/ with a SKILL.md, but typing /my-skill reports “skill not found.” Or it shows up in the / menu but never auto-fires when you describe the matching task in plain language.
Fastest fix: run /skills inside the session. If your skill is not in that list, it is a path or frontmatter problem (jump to causes 1-2). If it is listed but tagged “user-only,” it has disable-model-invocation: true, so it can only be slash-invoked. If it is listed and invocable but Claude still ignores it, the description is too vague or got dropped from the skill budget (causes 4 and 7). Each of those is a one-line edit.
A few facts that changed in 2026 and trip people up: the command you type comes from the directory name, not the frontmatter name field; and edits to an existing SKILL.md now take effect live in the running session — no restart needed (as of June 2026, Claude Code v2.1.x). The old “restart and grep claude --debug” advice is out of date.
Which bucket are you in
Run /skills first, then read across:
What /skills shows | Likely cause | Go to |
|---|---|---|
| Skill is absent entirely | Wrong path, or frontmatter the parser rejected | Causes 1-2 |
| Listed, badge says “user-only” | disable-model-invocation: true set | Cause 3 |
Listed, invocable, /name works but no auto-fire | Description too vague | Cause 4 |
| Listed, but you see “skill descriptions dropped” warning | Skill listing budget overflow | Cause 7 |
| Two skills with the same name | One shadows the other | Cause 6 |
Common causes
Ordered by hit rate, highest first.
1. Skill file is not at the expected path
Claude Code scans a fixed set of locations: ~/.claude/skills/<skill-name>/SKILL.md (personal), project-local .claude/skills/<skill-name>/SKILL.md (and the same path in every parent directory up to the repo root), and plugin-provided skills under <plugin>/skills/<skill-name>/SKILL.md. A skill saved as a flat file ~/.claude/skills/my-skill.md (no folder) or under ~/.claude/skill/ (singular, wrong dir name) is never picked up. This is the single most common case — the official docs list it first too.
How to judge: Run ls ~/.claude/skills/ and confirm each entry is a directory containing a file named exactly SKILL.md. A bare .md file directly in skills/ will not load.
2. Missing or malformed frontmatter
SKILL.md should start with YAML frontmatter between --- markers. Technically every field is optional and Claude will fall back to the directory name and the first paragraph of body text, but a broken block — a missing closing ---, smart quotes pasted from a doc, or tabs instead of spaces — makes the parser reject the file so it never appears in /skills.
How to judge: Open SKILL.md. Confirm the first line is ---, there is a matching closing --- before the body, and the only quotes are straight " not curly. Run /doctor — it flags schema errors and invalid keys in your config.
3. The skill is set to manual-only (or hidden)
If the skill is in /skills but never auto-fires, check its badge. disable-model-invocation: true in the frontmatter means only you can invoke it with /name; Claude will not trigger it on its own, and its description is dropped from context entirely. The mirror image, user-invocable: false, hides it from the / menu (Claude can still call it).
How to judge: In /skills, a “user-only” label means the model cannot trigger it. Open the frontmatter and look for disable-model-invocation: true. Remove it if you want auto-firing.
4. Description is too vague for auto-trigger
For auto-invocation, Claude matches your request against each skill’s description (plus optional when_to_use). A one-word description like “helper” gives the model nothing to match, so it only fires when slash-invoked. Put the key use case first: the combined description + when_to_use text is truncated at 1,536 characters in the listing (configurable via maxSkillDescriptionChars).
How to judge: Read your description aloud. If it does not name concrete triggers (“when the user asks to deploy”, “for SQL migrations”), it is too vague. Test by phrasing your request to closely echo the description.
5. Allowed-tools confusion (usually a non-issue now)
allowed-tools does not restrict which tools the skill can use — it pre-approves the listed tools so Claude can run them without a permission prompt while the skill is active. Every other tool is still available; it just prompts. So a mismatch between allowed-tools and the tools your body uses will not stop the skill from loading. If your steps still need approval mid-run, add those tools to allowed-tools (e.g. allowed-tools: Bash(git commit *) Read).
How to judge: If the skill loads but pauses for permission on a tool, that tool is missing from allowed-tools, not blocked.
6. A duplicate skill is shadowing yours
When skills share a name across levels, precedence is: enterprise > personal > project. Plugin skills are namespaced (plugin-name:skill-name) so they cannot collide with your own. If both ~/.claude/skills/foo/ and .claude/skills/foo/ exist, the personal one wins and the project one is hidden.
How to judge: Look at /skills for two entries that resolve to the same /foo. Rename one (prefix it, e.g. my-foo) so both are reachable.
7. Skill descriptions dropped by the listing budget
New in 2026: Claude loads every skill’s name, but the descriptions share a context budget that defaults to 1% of the model’s context window (the skillListingBudgetFraction setting). If you have many skills and the budget overflows, the least-used skills lose their descriptions, so the model can no longer auto-match them — you will see a “skill descriptions dropped” warning at startup. The skill still works when slash-invoked.
How to judge: Run /doctor; it reports whether the budget is overflowing and which skills are affected. Raise it with "skillListingBudgetFraction": 0.02 in ~/.claude/settings.json (2% costs ~2,000 extra tokens per session), or set low-priority skills to "name-only" in skillOverrides.
Before you start
- Confirm you are using the Claude Code CLI, not Claude.ai web (skills load differently there).
- You do not need to restart for an edit to an existing
SKILL.md— live change detection picks it up within the session. You do need to restart only if you just created a top-level skills directory that did not exist when the session started. - Note whether you expect slash invocation, auto-trigger, or both — they fail for different reasons.
Information to collect
- Claude Code version:
claude --version(live reload and/skillsrequire a recent 2.1.x). - OS and shell:
uname -aplusecho $SHELL. - Output of
ls -la ~/.claude/skills/andls -la .claude/skills/from your project root. - The full contents of your
SKILL.md, especially the frontmatter block. - Inside the session:
/skills(what loaded) and/context(whether the description is in context). - Any error printed when you typed
/your-skill-name.
Step-by-step fix
Step 1: Verify the on-disk layout
ls -la ~/.claude/skills/
ls -la ~/.claude/skills/my-skill/
You should see a directory my-skill/ containing SKILL.md. If instead you see my-skill.md as a flat file, move it into a folder — the directory name becomes your /command:
mkdir -p ~/.claude/skills/my-skill
mv ~/.claude/skills/my-skill.md ~/.claude/skills/my-skill/SKILL.md
Step 2: List what actually loaded
Inside the session, run:
/skills
This is the authoritative loaded-skills list (project, user, and plugin sources), with a badge showing whether each is model-invocable. If your skill is missing here, the loader rejected it — continue to Step 3. If it is present but “user-only,” skip to Step 5.
Step 3: Validate the frontmatter
Open SKILL.md and check the top block:
---
name: my-skill
description: Summarize uncommitted changes and flag risky edits. Use when the user asks what changed, wants a commit message, or asks to review their diff.
---
The opening and closing --- are required for a frontmatter block to parse. Avoid tabs and smart quotes. Then run /doctor — it validates config files and reports schema errors or invalid keys; press f to send the report to Claude for a guided fix.
Step 4: Confirm the skill is visible to the model
Ask, in plain language:
What skills are available?
If your skill is named in the answer, Claude can see and auto-invoke it. If /skills lists it but this answer omits it, its description was likely dropped by the listing budget — go to Step 7.
Step 5: Improve the description for auto-trigger
Rewrite the description to include concrete trigger phrases, key use case first:
description: |
Deploy a service, run database migrations, or invalidate the CDN
cache. Use when the user says "deploy api to staging" or
"run migration 0042".
Specific triggers dramatically increase auto-firing. If you set disable-model-invocation: true earlier for safety, remember that blocks all auto-firing on purpose — remove it if you want the model to trigger the skill.
Step 6: Check allowed-tools coverage (only if it pauses for permission)
allowed-tools does not gate loading; it only pre-approves tools. If your skill runs but stops for approval on, say, git commit, list that tool:
allowed-tools: Bash(git add *) Bash(git commit *) Read
Omit allowed-tools entirely to fall back to your normal permission prompts.
Step 7: Resolve a dropped or shadowed skill
/doctor
If /doctor reports “skill descriptions dropped,” your listing budget overflowed. Either raise it or demote skills you rarely use:
{
"skillListingBudgetFraction": 0.02,
"skillOverrides": { "legacy-context": "name-only" }
}
For a true name collision (two skills resolving to the same /command), rename one of the folders with a personal prefix, e.g. my-deploy instead of deploy.
Step 8: When all else fails, isolate with safe mode
If a skill that looks correct still will not load, launch a clean session to rule out a conflicting hook, plugin, or setting:
claude --safe-mode
Safe mode (v2.1.169+) disables all customizations — CLAUDE.md, skills, plugins, hooks, MCP. If your skill works after you re-enable only it, something else in your config was interfering. As a deeper test, point Claude at an empty config dir: cd /tmp && CLAUDE_CONFIG_DIR=/tmp/claude-clean claude.
How to confirm it’s fixed
/skillslists the skill with no “user-only” badge (unless you intended manual-only)./my-skillruns and prints the expected output.- Asking “What skills are available?” names your skill, and a natural-language prompt matching the description triggers it within a turn or two.
/doctorshows no schema errors and no “descriptions dropped” warning for this skill.- It works in a fresh session, not just the one you edited in.
Long-term prevention
- Keep a
skills/README.mdin your dotfiles listing every skill and its trigger phrases. - Run
/doctorperiodically; it catches frontmatter schema errors and budget overflow before they bite. - Use
disable-model-invocation: trueonly for skills with side effects (deploy, commit) that you want to fire manually. - Demote rarely-used skills to
"name-only"inskillOverridesto keep budget for the ones you depend on. - Version-control your
~/.claude/skills/directory.
Common pitfalls
- Assuming you must restart after an edit. You don’t — existing
SKILL.mdedits reload live. Only a brand-new top-level skills directory needs a restart. - Thinking the frontmatter
namesets the slash command. It sets the display label; the directory name sets/command(except for a plugin-rootSKILL.md). - Copy-pasting frontmatter from a blog and importing smart quotes or tabs.
- Setting
disable-model-invocation: trueand then wondering why auto-trigger never happens. - Writing a 300-word description; it gets truncated at 1,536 characters and may be dropped under budget pressure. Keep it tight and front-load the trigger.
- Putting the skill in
~/.claude/skill/(singular) by typo.
FAQ
- Where does Claude Code look for skills?
~/.claude/skills/<name>/SKILL.md(personal),.claude/skills/<name>/SKILL.mdin your project and its parent directories (project), and<plugin>/skills/<name>/SKILL.md(plugin). Precedence when names collide: enterprise > personal > project. - Do I need to restart after adding a skill? No, for edits to an existing
SKILL.md— live change detection applies them in the running session. Yes, only if you created a top-level skills directory that did not exist when the session started. - What sets the slash command name? The skill’s directory name, not the frontmatter
namefield..claude/skills/deploy-staging/SKILL.mdbecomes/deploy-stagingregardless ofname:. - Why does my skill show in
/skillsbut never auto-fire? Either it hasdisable-model-invocation: true(manual-only), or its description is too vague or was dropped by the listing budget. Run/doctorto check the budget; tighten the description otherwise. - How do I list all loaded skills? Run
/skillsin the session, or ask “What skills are available?” to see what the model can auto-invoke./contextshows whether descriptions are occupying context. - What is the “skill descriptions dropped” warning? Skill descriptions share a budget that defaults to 1% of the context window. With many skills it overflows and the least-used lose their descriptions. Raise
skillListingBudgetFraction(e.g.0.02) or set low-priority skills to"name-only". - Can two skills share a name? Across levels, the higher-precedence one wins and hides the other. Plugin skills are namespaced (
plugin:name) so they never collide. Rename one of your own to avoid shadowing.
Related
- Claude Code settings.json not loading
- Claude Code subagent result not relayed
- Claude Code hook blocks edit unexpectedly
- Claude Code permissions prompt loop
- Claude Code tool execution hangs
External references: Extend Claude with skills and Debug your configuration (official Claude Code docs).