[BUG] Plugin marketplace git-sync silently drops invalid skills and reports success — no validation error surfaced at all

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Jul 31, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Skills that fail server-side validation are silently omitted when a plugin
marketplace is synced from a git repository to claude.ai, while the sync reports
success.

Concretely: a plugin with five skills, two of which had angle-bracket
placeholders (<label>, <topic>) in their description frontmatter. After
syncing:

  • The sync reported success.
  • The marketplace menu showed the correct synced commit.
  • Only three skills appeared. The two with angle brackets were absent.
  • No error was shown anywhere — not a specific one, not a generic "Plugin

validation failed", and nothing in the plugin detail view indicating a skill
had been dropped.

  • claude plugin validate passed, and Claude Code loaded all five skills.

A partial failure is reported as a complete success.

This is the same underlying validation described in #56376 (server
validation_errors[] swallowed, UI shows only a generic message), but on a
different code path with a worse failure mode. The upload path discards the
server's error detail; the git-sync path discards the existence of the error.

Filing as a new issue because the existing reports form a chain that ends in a
bot-staleness close rather than a triage decision: #63081 → duplicate of #61283 →
duplicate of #56517 → duplicate of #56376, which was labelled stale on May 19
and closed "not planned" by github-actions on June 2 with "Closing for now —
inactive for too long. Please open a new issue if this is still relevant."

What Should Happen?

In order of value:

  1. Surface dropped skills on the sync path. The claude.ai plugin detail view

should report that a skill was rejected, naming the file and the field — the
sync-path equivalent of what #56376 requested for uploads. Today the
synced-commit indicator actively argues against the skill being at fault.

  1. Have claude plugin validate enforce what the platform enforces. The rule

is deterministic and cheap to check statically. Catching it locally would
prevent the failure from reaching either surface.

  1. Optionally, escape on render rather than reject on input, as #63081

suggests. Items 1 and 2 are worth doing regardless of what is decided here.
Note the trigger set is broader than angle brackets: #56517 reports the same
rejection for a URL in description, and #64305 reports the Cowork MCP
client discarding a structured server validation error. Fixing the reporting
covers all current and future triggers; fixing angle brackets specifically does
not. The URL case also argues against reading this as a deliberate guard
on XML-like content in frontmatter: a URL contains no angle brackets, so
whatever rejects these strings is broader than a prompt-injection guard.

Error Messages/Logs

None. That is the bug.

No error in the claude.ai UI. No "Plugin validation failed" (unlike the bundle
upload path). No indication in the plugin detail view that any skill was
rejected. The marketplace menu reports "Synced commit: <current HEAD>", so the
sync self-reports as fully successful.

Locally, on every commit including the failing ones:
 
  $ claude plugin validate .
  ✔ Validation passed with warnings          # unrelated: no version in plugin.json

  $ claude plugin validate ./plugins/<plugin>
  ✔ Validation passed with warnings          # same warning; no mention of the
                                             # skills that will be dropped

Claude Code /skills listed all five skills, including the two that claude.ai
silently omitted.

Steps to Reproduce

  1. Create a marketplace repo containing .claude-plugin/marketplace.json with a

single plugin at a relative source, and under that plugin two skills that are
identical except for the description frontmatter:

  • skills/alpha/SKILL.mddescription: Use when the user types "Go <label>"
  • skills/beta/SKILL.mddescription: Use when the user types "Go [label]"
  1. Run claude plugin validate . and claude plugin validate ./plugins/<plugin>.

Both pass. Neither mentions alpha.

  1. Add the marketplace in Claude Code and install the plugin. /skills lists

both alpha and beta.

  1. On claude.ai: Customize → Plugins → Personal → "+" → Add marketplace → "Add

from a repository" → the same repo. Install the same plugin.

  1. Open the plugin detail view, or type / in a new conversation.

Result: beta is present, alpha is absent, no error is shown anywhere, and
the marketplace ··· menu reports the correct commit as synced.

  1. Change <label> to [label] in alpha, commit, push, and click "Check for

updates".
Result: alpha now appears. No other change was made.

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.220 (Claude Code)

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Xterm

Additional Information

The affected surface is claude.ai web (Customize → Plugins → Personal), not
Cowork and not an organization marketplace. The marketplace is a private
GitHub repo synced through the Claude GitHub App, scoped to that single
repository, with "Sync automatically" enabled. Plugin sources are relative paths
inside the marketplace repo, and plugin.json intentionally omits version so
versions resolve from the commit SHA.

Isolation method. Five skills, two missing. Changed one of the two from
<label> to [label] as the only modification in that commit, left the other
untouched as a control, and re-synced: the changed skill appeared, the control
stayed absent. Then applied the same one-line change to the control and it
appeared as well.

Why the reporting matters more than the validation rule. Because the sync
self-reports as successful and displays the correct commit, the visible symptom
is "my skills didn't sync." That sends you to webhooks, GitHub App permissions,
repository access, and marketplace state before the skill file is ever suspected.
Diagnosis took about an hour and ended in a five-way controlled test to isolate
two characters. Four prior reporters each bisected the same behaviour
independently — #56517 over an unknown number of builds, #61283 over ~8, #63081
over ~10 — which is the cost of the missing error message, repeated.

On the deduplication chain. #63081, #61283, and #56517 are each closed as
duplicates, terminating at #56376, which a bot closed as "not planned" for
inactivity. The reports were never triaged to a human decision; they were
consolidated into a ticket that then aged out. Filing fresh per that bot's
instruction rather than commenting on a closed duplicate.

View original on GitHub ↗