learnaiwithrafa
ClaudeSkills

When a Claude Skill Never Fires: Six Causes, in Diagnostic Order

The cruelest failure mode is a broken skill whose slash command works perfectly. Malformed frontmatter still loads the body, so /my-skill runs fine while Claude has no description to match against — and you conclude the skill is healthy. Here is the order to check things in.

8 min read4 sources
  • #claude-code
  • #skills
  • #debugging
  • #troubleshooting

Here is the failure mode that wasted the most of my time, and it sits in the docs as one sentence: if a skill's YAML frontmatter is malformed, Claude Code loads the body with empty metadata. Typing /my-skill still works — the instructions are right there — but Claude has no description to match your phrasing against, so it will never reach for the skill on its own. You test it by hand, it works, and you conclude the skill is healthy. It isn't. Run claude --debug and the parse error is waiting in the log.

That's the shape of most skill bugs. The skill is rarely wrong; the routing is. So debug it like routing — from "did this file load at all" outward. Never start at the instructions.

The order to check things in

Run these in sequence and stop at the first surprise. Ten minutes, no guessing.

  1. /context — is there a Skills row at all? This tells you whether anything loaded, before you start theorizing about wording.
  2. /skills — does yours appear? The most common reason it doesn't: the file sits at .claude/skills/my-skill.md instead of .claude/skills/my-skill/SKILL.md. A skill is a directory with SKILL.md inside, not a loose markdown file.
  3. Read the badge in /skills. A user-only label means disable-model-invocation: true is set, and per the docs that keeps the description out of Claude's context entirely — you can type it, Claude cannot fire it. That's a setting, not a bug, and it's usually inherited from whichever skill you copied to get started.
  4. claude --debug — for the empty-metadata case above. Any YAML error (an unquoted colon in the description, a tab, a curly quote pasted from a doc) lands you here.
  5. Ask, inside the session: "What skills are available?" If it shows up with a description that doesn't sound like how you actually type the request, you've found it.
  6. claude --safe-mode — launches with every customization disabled. If the problem disappears, something in your config is causing it; if it survives, stop blaming the skill file.

Cause 1 — the description is a topic label, not a trigger

The description is the entire routing table. Anthropic's authoring guide is blunt about the mechanics: it gets injected into the system prompt, so write it in third person — "Generates commit messages by analyzing git diffs", not "I can help you write commit messages", which the docs say causes discovery problems outright.

The fix isn't to write it better in the abstract. Open your terminal history, find the last three times you asked for this work in your own words, and put those words in the description. Claude matches against the phrasing you actually use, not the phrasing you imagined at authoring time.

Cause 2 — your description got silently truncated

This one is invisible, and it only starts biting once you have a real collection. Claude Code loads a listing of every skill name plus description into context, and that listing gets a character budget of 1% of the model's context window. When it overflows, Claude Code drops descriptions starting with the skills you invoke least — so the newest skill, the one you're debugging right now, is first in line to lose the exact keywords it needs. Each entry is also capped at 1,536 characters regardless of budget.

Run /doctor for an estimate of the listing's context cost and its biggest contributors. Then pick one of three moves: raise skillListingBudgetFraction (0.02 = 2%), set low-value skills to "name-only" in skillOverrides to free budget, or front-load the key trigger into the first sentence so truncation eats the tail instead of the trigger.

Cause 3 — the wrong copy of the skill is winning

Names collide, and the precedence is not the one you'd guess: enterprise overrides personal, and personal overrides project. Read that again if you work on a team. A teammate's ~/.claude/skills/deploy/ beats the deploy skill you committed to the repo, and nothing warns either of you. A skill at any level also overrides a bundled one — drop code-review into your project and it replaces Claude Code's own /code-review.

Two escapes. Plugin skills are namespaced plugin-name:skill-name, so they can't collide with anything. And in a monorepo, a nested apps/web/.claude/skills/deploy/ shows up under the qualified name /apps/web:deploy while plain /deploy still runs the root one.

Cause 4 — it's silenced from settings, not from the file

skillOverrides in .claude/settings.local.json controls visibility independently of the skill's own frontmatter — and the /skills menu writes that file for you when you cycle a skill's state with Space. A value of "off" hides it from Claude and from the / menu. Permission deny rules cut it off from a different angle: a bare Skill deny kills every skill, Skill(deploy *) kills one. Both survive a restart, and neither leaves any trace inside SKILL.md.

Cause 5 — it fires, then quietly stops mattering

Different symptom, different cause. When a skill is invoked, its rendered content enters the conversation as a single message and stays there. Claude Code does not re-read the file on later turns. So if your skill reads like a one-shot recipe, it's already spent by turn three. Write standing rules that hold for the whole task instead of steps that only make sense at the moment of invocation.

Compaction is the other half of this. When context fills up, Claude Code re-attaches the most recent invocation of each skill after the summary, keeping only the first 5,000 tokens of each, inside a combined 25,000-token budget filled from the most recently invoked backwards. On a long session, the skill you loaded first can be dropped entirely. If a skill stopped mattering right after a compaction, that's why — re-invoke it.

Cause 6 — you're debugging in a dirty session

The best-practices guide is explicit here: test with a fresh instance, because leftover context from authoring the skill masks the gaps in what you actually wrote down. The real check is a baseline comparison — collect a handful of realistic prompts, run each one with the skill available and again with it disabled, and compare the outputs.

If you'd rather not hand-run that loop, /plugin install skill-creator@claude-plugins-official automates it, including a description-tuning pass that generates should-trigger and should-not-trigger prompts and measures the hit rate. Anthropic reports it improved triggering on 5 of 6 public skills, cutting both false positives and false negatives. That's the number that convinced me description quality is something you measure, not something you have taste about.

Why this matters in review, not just in your terminal

If you're a Brazilian dev on a distributed US or EU team, a committed .claude/skills/ folder is one of the few artifacts that shows your judgment asynchronously — nobody has to share your timezone to see it. But a skill that never fires for anyone else is worse than no skill: it looks like tooling and behaves like decoration. The precedence and truncation traps above are exactly the ones that make a folder look healthy in your terminal and do nothing in anybody else's.

Today: pick the skill you're least sure about and run /skills. Check two things — that its source is where you think it is, and that its description contains the literal phrase you last used to ask for that work. Then open a fresh session, type that phrase, and watch whether it fires. If it doesn't, you now know which of the six causes to open.

Sources