[BUG] `claude plugin validate` hard-errors on `argument-hint` bracket syntax that the runtime accepts (coerces to string)

Open 💬 1 comment Opened Jun 24, 2026 by topp

Summary

claude plugin validate rejects a SKILL.md whose argument-hint frontmatter value uses the documented bracket form (e.g. argument-hint: [issue-number]), reporting a fatal "YAML frontmatter failed to parse" error. But the runtime accepts the same file — it coerces the argument-hint value to a string (the fix shipped for #25826 / #22161). The bundled validator was never aligned with that runtime coercion, so it produces false positives for a pattern that:

  1. the official docs show as the example (skills.md, argument-hint row: "Example: [issue-number] or [filename] [format]"), and
  2. the runtime explicitly tolerates by design.

Net effect: skills that load and run perfectly are reported as broken by the project's own validator.

Environment

  • Claude Code v2.1.187 (macOS)
  • Runtime coercion fix referenced: #25826 / #22161 (closed, fixed 2026-02-18)

Steps to reproduce

  1. Create a skill my-plugin/skills/demo/SKILL.md:

``yaml
---
name: demo
description: Demo skill.
argument-hint: [<plan-path>] "<note>"
---
Body.
`
(The docs' own simpler example
argument-hint: [issue-number]` triggers the same behavior.)

  1. Run claude plugin validate ./my-plugin.

Actual

✘ Found 1 error:
  ❯ frontmatter: YAML frontmatter failed to parse: YAML Parse error: Unexpected token.
    At runtime this skill loads with empty metadata (all frontmatter fields silently dropped).
✘ Validation failed

Expected

Validation passes (optionally with a warning suggesting quotes), consistent with how the runtime handles the same value. The validator should apply the same argument-hint string-coercion the runtime uses, rather than hard-failing.

Why this is genuinely inconsistent

A leading [ makes the value a YAML flow sequence, so a strict parser rejects it — confirmed independently with PyYAML:

import yaml
yaml.safe_load('argument-hint: [<plan-path>] "<note>"')    # -> ParserError (block mapping)
yaml.safe_load("argument-hint: '[<plan-path>] \"<note>\"'") # -> OK (quoted, parses to string)

The point is not "the YAML is technically loose" — it's that the same product behaves two ways on the same input: the runtime coerces and runs fine (#25826), while the bundled claude plugin validate hard-errors. The validator should match the runtime.

The error text "At runtime this skill loads with empty metadata (all frontmatter fields silently dropped)" also appears inaccurate post-#25826 — the runtime renders name/description fine for these files.

Secondary: authoring guidance gap (docs)

The trap is easy to hit because the canonical guidance leads authors straight into it and never warns about quoting:

  • skills.md documents argument-hint with the example [issue-number] / [filename] [format] — i.e. it demonstrates exactly the unquoted bracket form that is parsed as a YAML flow sequence rather than a string.
  • The official plugin-dev plugin's skill-development skill documents SKILL.md frontmatter as only name / description / version — it never mentions argument-hint, and gives no guidance on quoting frontmatter values that begin with YAML-structural characters ([, {, *, &, !).

Suggested doc fixes:

  • In skills.md, quote the examples (argument-hint: '[issue-number]') and add a note: "Frontmatter values beginning with [, {, *, &, ! are interpreted as YAML structures; wrap them in single quotes to keep them as strings."
  • In the skill-development skill, list argument-hint (and allowed-tools, disable-model-invocation) among the frontmatter fields and add the same quoting note.

Related

  • #25826 / #22161 — runtime crash fixed via string coercion (validator not aligned)
  • #62127 — argument-hint not displayed in hint area (separate display issue)

View original on GitHub ↗

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