claude plugin list reports a plugin enabled with an installPath that no longer exists; payload is never re-materialized
Summary
claude plugin list --json reports a plugin as "enabled": true with aninstallPath that no longer exists, when the plugin's payload under<CLAUDE_CONFIG_DIR>/plugins/cache/<marketplace>/<plugin>/<version>/ has been
removed. Running the CLI again does not re-materialize it, so a plugin the
operator believes is installed contributes none of its skills.
Enablement and the materialized payload are tracked independently and never
reconciled, and there is no claude plugin doctor / verify to ask about the
second one.
Reproduction
export CLAUDE_CONFIG_DIR=$(mktemp -d)
MKT=$(mktemp -d)
mkdir -p "$MKT/.claude-plugin" "$MKT/plugins/demo/.claude-plugin" "$MKT/plugins/demo/skills/demo-skill"
cat > "$MKT/.claude-plugin/marketplace.json" <<'JSON'
{ "name": "repro", "owner": { "name": "repro" },
"plugins": [{ "name": "demo", "source": "./plugins/demo", "description": "fixture" }] }
JSON
cat > "$MKT/plugins/demo/.claude-plugin/plugin.json" <<'JSON'
{ "name": "demo", "version": "1.0.0", "description": "fixture" }
JSON
printf -- '---\nname: demo-skill\ndescription: fixture skill.\n---\nDemo.\n' \
> "$MKT/plugins/demo/skills/demo-skill/SKILL.md"
claude plugin marketplace add "$MKT"
claude plugin install demo@repro
ls "$CLAUDE_CONFIG_DIR/plugins/cache/repro/demo/1.0.0/skills/demo-skill/SKILL.md" # exists
# Remove only the payload; the enablement record survives.
rm -rf "$CLAUDE_CONFIG_DIR/plugins/cache/repro/demo/1.0.0"
claude plugin list --json
# [{"id":"demo@repro","enabled":true,
# "installPath":"<CLAUDE_CONFIG_DIR>/plugins/cache/repro/demo/1.0.0"}] <- path does not exist
claude plugin list # running the CLI again does not re-materialize it
ls "$CLAUDE_CONFIG_DIR/plugins/cache/repro/demo/1.0.0" 2>&1
# No such file or directory
claude plugin install demo@repro re-materializes the payload, and is
idempotent.
Impact
Any automation that provisions plugins into a scoped CLAUDE_CONFIG_DIR and
uses plugin list to decide whether an install is needed will skip the install
in exactly the state where it is required. Every command exits 0 and every
status field says healthy, so the failure only surfaces later as "the model
doesn't know about the skill".
We hit the identical defect in the Codex CLI
(https://github.com/openai/codex/issues/34321) and filed this because the cache
layout and the enabled-vs-materialized split are the same in both.
Workaround
Stat the payload instead of trusting plugin list:
ls "$CLAUDE_CONFIG_DIR/plugins/cache/<marketplace>/<plugin>"/*/skills/*/SKILL.md
and run claude plugin install <id> unconditionally when it is missing.
Suggested fixes
- Have
plugin liststatinstallPathand report the divergence rather than
"enabled": true.
- Reconcile on load: re-materialize an enabled plugin whose cached payload is
absent.
- Add a supported
claude plugin doctor/plugin list --verifyso automation
has a command to call instead of reading the cache directory layout.
Environment
- Claude Code CLI 2.1.215, Linux
- Local file-based marketplace (also reproduces with a git marketplace)
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗