metadata.pluginRoot in marketplace.json is ignored — bare plugin source fails validation despite being documented

Open 💬 1 comment Opened Jun 16, 2026 by justinpearsonappfolio

FOR HUMANS

Hi, Claude Code v2.1.179 detected a docs mismatch in how plugin metadata is structured, please see below. Have a nice day!

FOR A.I.

Summary

The docs say metadata.pluginRoot lets you write a bare plugin source (e.g. "formatter") instead of a ./-prefixed relative path. In practice the validator/resolver does not apply pluginRoot: a bare source fails with plugins.N.source: Invalid input. The two relevant doc rows also contradict each other.

Environment

  • Claude Code 2.1.178 and 2.1.179 (reproduces on both), macOS.

Docs in question

On https://code.claude.com/docs/en/plugin-marketplaces:

  • The optional-fields table says:

> metadata.pluginRoot — Base directory prepended to relative plugin source paths (for example, "./plugins" lets you write "source": "formatter" instead of "source": "./plugins/formatter")

  • The Plugin sources table says, for a relative-path string source:

> Local directory within the marketplace repo. Must start with ./.

These conflict: the first says a bare "source": "formatter" is allowed when pluginRoot is set; the second says a relative source must start with ./. The validator enforces the second and ignores pluginRoot.

Minimal reproduction

repro/
  .claude-plugin/marketplace.json
  plugins/formatter/.claude-plugin/plugin.json

plugins/formatter/.claude-plugin/plugin.json:

{ "name": "formatter", "version": "0.1.0", "description": "Minimal test plugin" }

.claude-plugin/marketplace.json — the exact documented pluginRoot example:

{
  "name": "pluginroot-repro",
  "owner": { "name": "repro" },
  "metadata": { "pluginRoot": "./plugins" },
  "plugins": [
    { "name": "formatter", "source": "formatter", "description": "bare source per docs pluginRoot example" }
  ]
}

Run:

claude plugin validate repro

Actual:

✘ Found 1 error:
  ❯ plugins.0.source: Invalid input
✘ Validation failed

Changing only the source to "./plugins/formatter" (leaving pluginRoot in place) → ✔ Validation passed. So pluginRoot has no effect; the ./ prefix is what's actually required.

Expected

Either:

  1. The resolver/validator applies metadata.pluginRoot so a bare source resolves to <pluginRoot>/<source> (matching the docs), or
  2. The docs drop the pluginRoot-enables-bare-source affordance and state plainly that every relative source must start with ./.

Impact beyond validation

A marketplace published with bare sources still "adds" successfully, but each affected plugin is then treated as an unreadable remote source. In the /plugin Discover panel this shows up as a missing description and Will install: · Component summary not available for remote plugin, so the plugins look broken to anyone browsing the marketplace — with no error pointing at the real cause. (Found this on an internal team marketplace; claude plugin validate was what surfaced the actual source: Invalid input.)

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗