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
- Open the offending SKILL.md and rewrite the frontmatter as a mapping: each field on its own line as `key: value`.
- Validate the YAML parses to an object before upload using `yq '. | type' SKILL.md` or a local SnakeYAML load.
- 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
- Always author SKILL.md frontmatter as `key: value` pairs, never a list or scalar.
- Run `yq '. | type' SKILL.md` in CI and fail if the type is not `!!map`.
- Use a JSON-schema-validated manifest tool instead of hand-writing YAML.
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
- SKILL.md is missing required YAML frontmatter
- Invalid SKILL.md frontmatter: {e.getMessage()}
- Skill manifest name '{manifestName}' does not match package
- Skill {name} version {version} already exists with a differe
- File path is required
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/158265ea070d1a81.
Report an issue: GitHub.