conductor-oss/conductor · error · IllegalArgumentException
Skill manifest is required
Error message
Skill manifest is required
What it means
Thrown by register() (via parseManifest) when the manifestJson argument is null or blank. The manifest is the @RequestPart("manifest") of the register multipart request and must be a non-empty JSON document. Without it there is no metadata to validate or version against.
Source
Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/service/SkillRegistryService.java:402
if (metadataDao.find(name, version).isPresent()) {
return version;
}
// Allow a checksum prefix in place of an exact version.
for (SkillDetail detail : metadataDao.listVersions(name)) {
if (detail.getChecksum() != null && detail.getChecksum().startsWith(version)) {
return detail.getVersion();
}
}
return version;
}
return metadataDao
.latestVersion(name)
.orElseThrow(() -> new IllegalArgumentException("Skill not found: " + name));
}
private Map<String, Object> parseManifest(String manifestJson) {
if (manifestJson == null || manifestJson.isBlank()) {
throw new IllegalArgumentException("Skill manifest is required");
}
try {
return MAPPER.readValue(manifestJson, MAP_TYPE);
} catch (IOException e) {
throw new IllegalArgumentException("Invalid skill manifest JSON: " + e.getMessage(), e);
}
}
private byte[] readPackage(MultipartFile packageFile) {
try {
byte[] bytes = packageFile.getBytes();
if (bytes.length > maxPackageBytes) {
throw new IllegalArgumentException(
"Skill package exceeds max size of " + maxPackageBytes + " bytes");
}
return bytes;
} catch (IOException e) {
throw new IllegalArgumentException(View on GitHub (pinned to cf7c3e4a8a)
Solutions
- Include a non-empty JSON manifest part in the multipart request.
- At minimum send {} if you want SKILL.md frontmatter to drive all fields — but the version will fall back to a checksum prefix.
- Verify the multipart form has both 'manifest' (text) and 'package' (file) parts.
Example fix
# before: only the package part sent
curl -F 'package=@skill.zip' /api/skills/register
# after: include a manifest part
curl -F 'manifest={"name":"foo","version":"1.0.0"}' -F 'package=@skill.zip' /api/skills/register Defensive patterns
Strategy: validation
Validate before calling
// Client-side: ensure manifest part is present and non-blank
if (manifest == null || manifest.isBlank()) {
throw new IllegalArgumentException("manifest part is required");
} Type guard
static boolean manifestPresent(String m) { return m != null && !m.isBlank(); } Prevention
- Always send both 'manifest' and 'package' parts in the multipart form.
- Default the manifest to '{}' if you want SKILL.md to drive all fields.
When it happens
Trigger: POST /api/skills/register with the 'manifest' part missing, empty, or containing only whitespace. Spring binds a missing required @RequestPart to a 400 before this in some configs, but a present-but-blank part reaches here.
Common situations: Client sends the 'package' part but forgets the 'manifest' part; a manifest field that is an empty string in a form builder; content-type negotiation causing the manifest part to deserialize as blank.
Related errors
- Skill manifest name '{manifestName}' does not match package
- Invalid skill manifest JSON: {e.getMessage()}
- Skill {name} version {version} already exists with a differe
- File path is required
- skillRef is required
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/6a90babbefe61f99.
Report an issue: GitHub.