The Specific Situation

Your SKILL.md file has what looks like valid frontmatter, but Claude treats the entire file as body text. The description is missing from “What skills are available?” The skill never auto-triggers. It still works when invoked manually with /name, but only because Claude reads the markdown body – all frontmatter metadata is lost. The file has a silent YAML parse error.

Technical Foundation

SKILL.md frontmatter follows standard YAML between --- delimiters. When the YAML fails to parse, Claude Code silently falls back to treating the entire file as body content. There is no error message, no warning, no log entry. The skill still “works” but without any metadata: no description (no auto-triggering), no allowed-tools (permission prompts for every tool), no disable-model-invocation (may auto-trigger unexpectedly or not at all).

All frontmatter fields are optional, so Claude Code cannot distinguish between “no frontmatter” and “broken frontmatter.” Both look the same: a skill with no metadata.

The Working SKILL.md

A correctly formatted frontmatter:

---
name: deploy-staging
description: >
  Deploy the application to the staging environment. Use when
  the user says "deploy to staging" or "push to stage".
disable-model-invocation: true
allowed-tools: Bash(npm run build *) Bash(npm test *)
argument-hint: "[branch-name]"
---

# Deploy to Staging

Deploy $ARGUMENTS to the staging environment.

Key formatting rules:

Error 1: Tabs Instead of Spaces

YAML requires spaces for indentation. A single tab causes a parse failure.

# BROKEN (tab character before "Deploy"):
---
name: deploy
description:	Deploy the application    # TAB before "Deploy"
---

# FIXED (spaces only):
---
name: deploy
description: Deploy the application
---

Diagnostic: Many editors show tabs vs spaces. In VS Code, enable “Render Whitespace” (editor.renderWhitespace: all). In the terminal:

cat -A .claude/skills/deploy/SKILL.md | head -5
# Tabs show as ^I

Error 2: Unquoted Special Characters

YAML interprets colons, brackets, and other characters specially.

# BROKEN (colon in description triggers YAML mapping):
---
description: Deploy to staging: run tests first
---

# FIXED (wrap in quotes or use folded scalar):
---
description: "Deploy to staging: run tests first"
---

# Also FIXED (folded scalar):
---
description: >
  Deploy to staging: run tests first
---

Characters that need quoting when they appear in values: : { } [ ] , & * # ? | - < > = ! % @

Error 3: Missing or Malformed Delimiters

The frontmatter must start on line 1 with exactly --- and end with exactly ---.

# BROKEN (space before delimiter):
 ---
name: deploy
---

# BROKEN (extra characters):
--- yaml
name: deploy
---

# BROKEN (missing closing delimiter):
---
name: deploy
# No closing ---

# CORRECT:
---
name: deploy
---

Error 4: Boolean Values as Strings

YAML auto-converts true, false, yes, no, on, off to booleans. This usually works correctly for SKILL.md fields, but can cause issues with unexpected values.

# This works (boolean intended):
disable-model-invocation: true

# This might not work (string "yes" auto-converted to boolean):
disable-model-invocation: yes  # Parsed as true (usually OK)

# Potential issue with description containing boolean-like words:
description: yes, this skill deploys  # "yes" may be parsed as boolean
# FIXED:
description: "yes, this skill deploys"

Error 5: Invalid Field Names

Field names must match exactly. A typo means the field is ignored (not an error, but behavior is wrong).

# WRONG field names (silently ignored):
---
descriptions: Deploy the app          # Should be: description
disable_model_invocation: true         # Should be: disable-model-invocation
allowedTools: Bash(git *)              # Should be: allowed-tools
when-to-use: trigger phrases           # Should be: when_to_use
---

# CORRECT field names:
---
description: Deploy the app
disable-model-invocation: true
allowed-tools: Bash(git *)
when_to_use: trigger phrases
---

Note the inconsistency: some fields use hyphens (disable-model-invocation, allowed-tools), others use underscores (when_to_use). This matches the official specification.

Validation Script

Validate your SKILL.md frontmatter from the terminal:

python3 << 'SCRIPT'
import yaml
import sys

path = ".claude/skills/deploy/SKILL.md"
with open(path) as f:
    content = f.read()

if not content.startswith("---"):
    print(f"ERROR: {path} missing opening --- delimiter")
    sys.exit(1)

parts = content.split("---", 2)
if len(parts) < 3:
    print(f"ERROR: {path} missing closing --- delimiter")
    sys.exit(1)

try:
    meta = yaml.safe_load(parts[1])
    if meta is None:
        print(f"WARNING: {path} has empty frontmatter")
    else:
        print(f"OK: Parsed {len(meta)} fields: {list(meta.keys())}")
        valid_fields = {
            "name", "description", "when_to_use", "argument-hint",
            "disable-model-invocation", "user-invocable", "allowed-tools",
            "model", "effort", "context", "agent", "hooks", "paths",
            "shell", "version", "compatibility"
        }
        unknown = set(meta.keys()) - valid_fields
        if unknown:
            print(f"WARNING: Unknown fields: {unknown}")
except yaml.YAMLError as e:
    print(f"ERROR: YAML parse failed: {e}")
    sys.exit(1)
SCRIPT

Common Problems and Fixes

Frontmatter looks correct but still fails: Check for invisible characters (zero-width spaces, BOM markers). Re-type the file from scratch or convert encoding: iconv -f utf-8 -t utf-8 -c SKILL.md > SKILL.md.clean.

Field value wraps to next line unexpectedly: Long values need proper multi-line syntax. Use > for folded (newlines become spaces) or | for literal (newlines preserved).

allowed-tools field not working: Space-separated syntax (Bash(git *) Read Grep) and YAML list syntax both work. If using a list, indent properly under the field name.

Validation passes but skill still has no metadata: Double-check the file path. You may be validating a different file than the one Claude Code reads.

Production Gotchas

Claude Code provides no YAML validation feedback. The only way to confirm frontmatter parsed correctly is to check behavior: ask “What skills are available?” and verify the description appears exactly as written. If it shows the first paragraph of the body instead of the frontmatter description, the frontmatter failed to parse.

Some editors auto-insert BOM (Byte Order Mark) characters at the beginning of UTF-8 files. This invisible character before --- breaks frontmatter parsing. Use file SKILL.md to check: it should say “UTF-8 Unicode text” not “UTF-8 Unicode (with BOM) text.”

Checklist