conductor-oss/conductor · error · IllegalArgumentException
Skill file must be UTF-8 text: {path}
Error message
Skill file must be UTF-8 text: {path} What it means
Thrown by decodeUtf8 when a file inside an uploaded skill package cannot be decoded as UTF-8. The decoder is configured with CodingErrorAction.REPORT for both malformed and unmappable sequences, so any byte sequence that is not valid UTF-8 (e.g. a Windows-1252 smart quote, a Latin-1 byte, or a binary asset mislabeled as text) causes the failure.
Source
Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/service/SkillRegistryService.java:597
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();
} catch (CharacterCodingException e) {
throw new IllegalArgumentException("Skill file must be UTF-8 text: " + path, e);
}
}
private boolean isRootAgentFile(String path) {
return !path.contains("/") && path.endsWith("-agent.md");
}
private boolean isScriptFile(String path) {
if (!path.startsWith("scripts/")) {
return false;
}
String filename = path.substring("scripts/".length());
return !filename.isBlank() && !filename.contains("/");
}
private boolean isResourceFile(String path) {
if (!path.contains("/")) {
return true;View on GitHub (pinned to cf7c3e4a8a)
Solutions
- Re-save the offending file as UTF-8 without BOM (`iconv -f CP1252 -t UTF-8 file.md`).
- Double-check the file is actually text — if it is binary, give it a binary extension so it is not routed through decodeUtf8.
- Strip BOM bytes (`sed -i '1s/^\xEF\xBB\xBF//' file.md`).
Example fix
// before: SKILL.md saved as Windows-1252 with smart quotes // convert on the command line iconv -f CP1252 -t UTF-8 SKILL.md -o SKILL.md.utf8 && mv SKILL.md.utf8 SKILL.md
Defensive patterns
Strategy: validation
Validate before calling
// Reject non-UTF-8 text entries before packaging.
private static void assertUtf8(String path, byte[] bytes) {
try {
StandardCharsets.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT)
.decode(ByteBuffer.wrap(bytes));
} catch (CharacterCodingException e) {
throw new IllegalArgumentException(path + " is not UTF-8", e);
}
} Type guard
boolean isUtf8(byte[] bytes) {
try {
StandardCharsets.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT)
.decode(ByteBuffer.wrap(bytes));
return true;
} catch (CharacterCodingException e) { return false; }
} Try / catch
try {
return skillRegistry.readFile(skillId, path);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("UTF-8")) return unsupportedMediaType(e.getMessage());
throw e;
} Prevention
- Save all skill text files as UTF-8 without BOM.
- Run `file -I <path>` in CI and assert charset is utf-8 for text extensions.
- Do not rename binary files to text extensions.
When it happens
Trigger: A package entry whose extension is in TEXT_EXTENSIONS (.md/.txt/.json/.yaml/.yml/...) contains non-UTF-8 bytes, or a binary file was renamed to a text extension. decodeUtf8 is invoked on it and the CharsetDecoder throws CharacterCodingException.
Common situations: Markdown authored on Windows saved as CP1252; a PNG or PDF slipped in with a `.txt` extension; a JSON file saved with a BOM or mojibake from a previous encoding round-trip.
Related errors
- Skill manifest name '{manifestName}' does not match package
- Skill {name} version {version} already exists with a differe
- File path is required
- skillRef is required
- Skill manifest is required
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/284502e1d9ca1119.
Report an issue: GitHub.