Skip to content

[Bug] SKILL.md YAML parsing fails silently when description contains unescaped double quotes #859

Description

@easonLiangWorldedtech

Problem (one or two sentences)

SKILL.md frontmatter parsing fails silently when the description field contains unescaped double quotes (e.g., "TDD", "test-driven development"), causing gray-matter/js-yaml to throw a YAMLException. The error message shown is misleading: "missing required 'name' field" — but the name IS present; it just couldn't be parsed because of the YAML syntax error.

Context (who is affected and when)

Affects any user or AI creating skills via the UI (createSkill tool), skill-writer mode, or manual file creation when the description contains double quotes. The skill silently fails to load — no visible indication in the Skills UI that parsing failed (only shown in Developer Tools console). This is a UX issue because users have no way to discover the real error without opening DevTools.

Reproduction steps

  1. Create a skill file at ~/.roo/skills/test-skill/SKILL.md with this content:
---
name: test-skill
description: Use when implementing features. Triggers on: "TDD", "test-driven development"
---

# Test Skill

Instructions here.
  1. Open Zoo Code → Settings → Skills tab
  2. The skill does not appear in the list (even though SKILL.md exists)
  3. Check Developer Tools console (Help → Toggle Developer Tools) — see:
    [Extension Host] Skill at <path> is missing required 'name' field
    

Programmatic reproduction:

cd /c/work/Zoo-Code
node -e "const matter = require('./node_modules/.pnpm/gray-matter@4.0.3/node_modules/gray-matter'); const fs = require('fs'); const content = fs.readFileSync('C:/Users/eason/.roo/skills/test-driven-development/SKILL.md', 'utf-8'); console.log(JSON.stringify(matter(content).data, null, 2));"

Output:

YAMLException: incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line at line 3, column 128

Expected result

Either:

  • The skill loads successfully (gray-matter handles unescaped quotes gracefully), OR
  • A clear error message explains the YAML parsing failure and points to the exact issue (e.g., "description contains unescaped double quotes")

Actual result

The skill silently fails to load. The console shows a misleading error: missing required 'name' field even though the name IS present in the frontmatter. Users/AI have no way to discover the real cause without opening DevTools.

Variations tried (optional)

  • Wrapping description in single quotes (description: 'Use when ... "TDD" ...') — works ✅
  • Using backticks instead of double quotes (Triggers on: \TDD``) — works ✅
  • Removing all quotes from description — works ✅

App Version

Current main (v3.58.x+)

API Provider

Not Applicable / Other

Relevant logs or errors

[Extension Host] Skill at C:\Users\eason\.roo\skills\test-driven-development is missing required 'name' field

Root cause: In src/services/skills/SkillsManager.ts line 102-115, when matter(fileContent) throws a YAML parsing error (from js-yaml@3.14.1), it's caught by the outer try/catch block at line 173. The specific YAML syntax error is lost — only "missing required 'name' field" is logged.

Proposed approach

Option A: Separate gray-matter parsing error (recommended)

In loadSkillMetadata(), catch the matter() parse error separately before checking fields:

// Before (line ~102-115)
const fileContent = await fs.readFile(skillMdPath, "utf-8")
const { data: frontmatter, content: body } = matter(fileContent)

if (!frontmatter.name || typeof frontmatter.name !== "string") {
    console.error(`Skill at ${skillDir} is missing required 'name' field`)
    return
}

// After
try {
    const fileContent = await fs.readFile(skillMdPath, "utf-8")
    let parsedResult
    try {
        parsedResult = matter(fileContent)
    } catch (parseError) {
        console.error(`Failed to parse SKILL.md at ${skillDir}: YAML syntax error - ${parseError.message}`)
        return
    }
    
    const frontmatter = parsedResult.data
    if (!frontmatter.name || typeof frontmatter.name !== "string") {
        console.error(`Skill at ${skillDir} is missing required 'name' field`)
        return
    }
    // ... rest of validation
} catch (error) {
    console.error(`Failed to load skill at ${skillDir}:`, error)
}

Option B: Auto-wrap description in createSkill() tool

In the handleCreateSkill method, automatically wrap the description value in single quotes if it contains double quotes or other special YAML characters. This prevents the issue for skills created via the UI tool.

Trade-offs / risks

  • Option A: Minimal risk — only changes error message clarity. No behavior change for valid SKILL.md files.
  • Option B: Could introduce unexpected formatting (e.g., single quotes in description might look odd). Requires careful escaping logic.
  • Both options are backward-compatible — existing valid skills continue to work.

Related

N/A — new issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions