Plugin Validation
A plugin that passes
claude plugin validatecan still fail to activate in production. Structural validation eliminates stupid failures; it doesn't guarantee quality. This skill covers both what the official validator checks and the additional contract the skillstack validator enforces.
When to use this skill
- "My plugin won't install" or "plugin fails to load" — structural errors
- "I want to add CI to my plugin repo" — validation tooling
- "Is my plugin.json correct?" — manifest fields
- "Why is my SKILL.md frontmatter invalid?" — frontmatter rules
- "How do I check cross-references?" — referenced files that don't exist
When NOT to use this skill
- Checking whether a skill activates correctly →
plugin-evaluation - Checking whether a skill's instructions are good →
skill-foundry - Debugging hook behavior →
plugin-hooks
Core principle
Structural validation is necessary but not sufficient. A structurally valid plugin can still fail because the frontmatter description doesn't trigger reliably, the skills are too long, or the hooks use exit code 1 instead of exit code 2. Structure is table stakes; evaluation is the real test.
What claude plugin validate checks
claude plugin validate . (run from the plugin directory) checks:
plugin.jsonJSON syntax and schema violations — missing requirednamefield, invalid component path typesSKILL.mdYAML frontmatter syntax — unclosed quotes, missing---delimiters, invalidnameformathooks/hooks.jsonsyntax — invalid JSON- Directory-structure errors — components inside
.claude-plugin/instead of plugin root
What it does NOT check:
- Whether
namein frontmatter matches the skill directory name - Whether files cited in SKILL.md (references, scripts) actually exist on disk
- Whether
descriptionmeets quality criteria (third-person, first 250 chars, trigger phrases) - Orphan catalog entries in
marketplace.json - Version drift across
plugin.json/registry.json/marketplace.json
What the skillstack validator adds
python3 plugin-dev/scripts/validate_plugin.py --plugin-dir ./your-plugin/ covers the gaps:
- Frontmatter
namematches the skill directory name (common drift source) - Every reference file cited in SKILL.md body (any
references/link) exists on disk - Plugin
nameinplugin.jsonmatches the plugin directory name - Multi-skill plugins: validates each sub-skill independently with
plugin/skillscoped errors
Run this before every git push or CI commit. See references/validation-checklist.md for the pre-ship checklist.
Running validation
# Official validator (inside your plugin directory)
claude plugin validate .
# Skillstack validator (any plugin, from repo root)
python3 plugin-dev/scripts/validate_plugin.py --plugin-dir ./my-plugin/
# JSON output for CI integration
python3 plugin-dev/scripts/validate_plugin.py --plugin-dir ./my-plugin/ --json
# Strict mode (fail on warnings too)
python3 plugin-dev/scripts/validate_plugin.py --plugin-dir ./my-plugin/ --strict
Exit codes: 0 = all pass, 1 = errors found, 2 = validator crashed, 3 = --strict mode warnings present.
Interpreting common errors
| Error | Cause | Fix |
|---|---|---|
plugin.json name 'X' does not match directory 'Y' |
plugin.json name field differs from directory |
Set name to match directory (kebab-case) |
SKILL.md frontmatter name 'A' does not match skill directory 'B' |
Frontmatter drift | Update frontmatter name: field |
SKILL.md cites <reference> which does not exist |
A link to a references/ file in the SKILL.md body points to a file that doesn't exist | Create the file at that path or remove the citation |
missing entry in registry.json |
Plugin not catalog-registered | Add entry to .claude-plugin/registry.json |
version drift: plugin.json=X vs registry.json=Y |
Version mismatch | Make all three version strings byte-equal |
missing skills/ directory |
Plugin has no skills | Create skills/ with at least one skill |
CI integration
Add this to your GitHub Actions workflow (mirror .github/workflows/ci.yml plugin-validation job):
- name: Validate plugin structure
run: python3 .github/scripts/validate_plugins.py # repo-level
# or for your own plugin repo:
# python3 plugin-dev/scripts/validate_plugin.py --plugin-dir . --strict
See references/validation-checklist.md for the full pre-ship checklist.
Anti-patterns
- Treating validation as sufficient — passing validation does not mean the plugin works. It means the structure is correct. You still need
plugin-evaluationto verify activation and output quality. - Skipping strict mode in CI —
--strictcatches warnings that become errors in future Claude Code versions. Run without--strictonly during active development; always run strict in CI. - Fixing symptoms instead of root causes — a
namemismatch between frontmatter and directory isn't fixed by renaming the directory. It's fixed by deciding which name is correct and aligning both. - Validating only once before shipping — every structural change (adding a reference, renaming a skill, updating plugin.json) can introduce new errors. Validate after every change.
- Ignoring dead reference warnings — a SKILL.md that cites
references/foo.mdbut the file doesn't exist means Claude will see a broken reference path. This silently degrades skill quality. - Not validating third-party plugins before use — run
validate_plugin.pyon any plugin you're considering installing. Structural errors predict runtime failures.
Decision tree
Plugin won't load at all?
→ Run `claude plugin validate .` → fix plugin.json syntax errors first
Plugin loads but skill doesn't activate?
→ Run validate_plugin.py --strict → check frontmatter name/description issues
→ If structure is clean → problem is description quality, use plugin-evaluation
Plugin works locally but fails on another machine?
→ Check for hardcoded paths (should use ${CLAUDE_PLUGIN_ROOT})
→ Check for missing dependencies in scripts/
→ Run validate_plugin.py on the installed copy
Plugin has multiple skills and some don't appear?
→ Verify each SKILL.md frontmatter name matches its directory
→ Check for duplicate skill names across the plugin
Plugin-Dev Authoring Toolkit by Viktor Bezdek — licensed under MIT.