conductor-oss/conductor · error · IllegalArgumentException

SKILL.md frontmatter must be a mapping

Error message

SKILL.md frontmatter must be a mapping

What it means

Thrown by SkillRegistryService when the YAML frontmatter of a SKILL.md file parses successfully but is not a key/value mapping (e.g. it is a bare list or scalar). The loader uses SnakeYAML's SafeConstructor, so a top-level YAML scalar like `hello` or a `- item` list yields a String/ArrayList rather than a Map, which this branch rejects. The check exists because the rest of the registry assumes structured metadata keyed by field name.

Source

Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/service/SkillRegistryService.java:577

            out.write(buffer, 0, read);
        }
        return out.toByteArray();
    }

    private Map<String, Object> parseSkillFrontmatter(String skillMd) {
        Matcher matcher = FRONTMATTER_PATTERN.matcher(skillMd);
        if (!matcher.matches()) {
            throw new IllegalArgumentException("SKILL.md is missing required YAML frontmatter");
        }
        try {
            LoaderOptions options = new LoaderOptions();
            Yaml yaml = new Yaml(new SafeConstructor(options));
            Object value = yaml.load(matcher.group(1));
            if (value == null) {
                return Map.of();
            }
            if (!(value instanceof Map<?, ?> map)) {
                throw new IllegalArgumentException("SKILL.md frontmatter must be a mapping");
            }
            return MAPPER.convertValue(map, MAP_TYPE);
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "Invalid SKILL.md frontmatter: " + e.getMessage(), e);
        }
    }

    private String decodeUtf8(String path, byte[] data) {
        try {
            return StandardCharsets.UTF_8
                    .newDecoder()
                    .onMalformedInput(CodingErrorAction.REPORT)
                    .onUnmappableCharacter(CodingErrorAction.REPORT)
                    .decode(ByteBuffer.wrap(data))
                    .toString();

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Open the offending SKILL.md and rewrite the frontmatter as a mapping: each field on its own line as `key: value`.
  2. Validate the YAML parses to an object before upload using `yq '. | type' SKILL.md` or a local SnakeYAML load.
  3. Ensure there is exactly one document and it starts with a key, not a `-`.

Example fix

// before (SKILL.md frontmatter)
---
- name: my-skill
  version: 1.0.0
---
// after
---
name: my-skill
version: 1.0.0
---
Defensive patterns

Strategy: validation

Validate before calling

// Validate SKILL.md frontmatter is a mapping before publishing.
private static void assertFrontmatterIsMap(String skillMd) {
    Matcher m = FRONTMATTER_PATTERN.matcher(skillMd);
    if (!m.matches()) throw new IllegalArgumentException("missing frontmatter");
    Object parsed = new Yaml(new SafeConstructor(new LoaderOptions())).load(m.group(1));
    if (parsed != null && !(parsed instanceof Map<?, ?>)) {
        throw new IllegalArgumentException(
            "frontmatter is " + parsed.getClass().getSimpleName() + ", must be a mapping");
    }
}

Type guard

boolean isFrontmatterMap(String skillMd) {
    Matcher m = FRONTMATTER_PATTERN.matcher(skillMd);
    if (!m.matches()) return false;
    Object v = new Yaml(new SafeConstructor(new LoaderOptions())).load(m.group(1));
    return v == null || v instanceof Map<?, ?>;
}

Try / catch

try {
    Map<String,Object> fm = skillRegistry.parseFrontmatter(skillMd);
} catch (IllegalArgumentException e) {
    // surface the message to the author; do not retry
    return badRequest(e.getMessage());
}

Prevention

When it happens

Trigger: Uploading/parsing a SKILL.md whose frontmatter between the `---` fences is a YAML list (`- name: x`) or a bare scalar (`just a string`). The FRONTMATTER_PATTERN matches, yaml.load() returns a non-Map, and the `instanceof Map<?,?>` guard fails.

Common situations: Author copy-pastes a list-style YAML config into frontmatter; a generator emits array metadata; frontmatter was hand-edited and demoted from `name: foo` to `- name: foo`.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/158265ea070d1a81. Report an issue: GitHub.