alibaba/nacos · error · IllegalArgumentException
Skill cannot be null
Error message
Skill cannot be null
What it means
SkillUtils.toZipBytes throws IllegalArgumentException when the Skill object itself is null. The method needs a non-null Skill to read its name, metadata, and resources for ZIP assembly.
Source
Thrown at api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillUtils.java:112
return skill.getSkillMd() == null ? EMPTY_STRING : skill.getSkillMd();
}
/**
* Convert Skill object to a ZIP byte array containing all skill files.
*
* <p>The ZIP structure mirrors the upload format:
* {@code skillName/SKILL.md}, {@code skillName/type/resourceName}, etc.
* Binary resources (marked with metadata encoding=base64) are decoded back to raw bytes.</p>
*
* @param skill the Skill object to convert
* @return ZIP file as byte array
* @throws IOException if ZIP creation fails
* @throws IllegalArgumentException if skill is null or skill name is blank
*/
public static byte[] toZipBytes(Skill skill) throws IOException {
if (skill == null) {
throw new IllegalArgumentException("Skill cannot be null");
}
if (StringUtils.isBlank(skill.getName())) {
throw new IllegalArgumentException("Skill name cannot be blank");
}
String skillName = skill.getName();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
// 1. SKILL.md
zos.putNextEntry(new ZipEntry(skillName + "/SKILL.md"));
zos.write(toMarkdown(skill).getBytes(StandardCharsets.UTF_8));
zos.closeEntry();
// 2. Resource files
if (skill.getResource() != null && !skill.getResource().isEmpty()) {
for (SkillResource resource : skill.getResource().values()) {
if (resource == null || StringUtils.isBlank(resource.getName())) {
continue;View on GitHub (pinned to 9b989acdf1)
Solutions
- Null-check the Skill object before calling toZipBytes.
- Return a 'not found' error to the caller if the skill lookup yields null.
- Use Optional.ofNullable(skill).orElseThrow() to make the null check explicit.
Example fix
// before
byte[] zip = SkillUtils.toZipBytes(skillService.find(name)); // may be null
// after
Skill skill = skillService.find(name);
if (skill == null) {
throw new NoSuchElementException("Skill not found: " + name);
}
byte[] zip = SkillUtils.toZipBytes(skill); Defensive patterns
Strategy: validation
Validate before calling
if (skill == null) {
throw new NoSuchElementException("Skill not found");
}
byte[] zip = SkillUtils.toZipBytes(skill); Type guard
public static boolean isSkillReadyForZip(Skill skill) {
return skill != null && skill.getName() != null && !skill.getName().trim().isEmpty();
} Try / catch
try {
byte[] zip = SkillUtils.toZipBytes(skill);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Skill cannot be null")) {
return notFound("Skill not found");
}
throw e;
} Prevention
- Null-check skill lookups before passing to toZipBytes.
- Use Optional.ofNullable(skill).orElseThrow() for explicit handling.
- Return domain-specific 'not found' errors instead of raw IllegalArgumentException.
When it happens
Trigger: Calling SkillUtils.toZipBytes(null). Happens when a skill lookup returns null (not found) and the result is passed directly without a null check. Also in stream pipelines where a filter or map produces null.
Common situations: A skill download/upload flow receives null because the skill was not found in storage. A deserialized Skill object is null due to a missing JSON body. A test passes null accidentally.
Related errors
- Skill name cannot be blank
- PARAMETER_MISSING
- PARAMETER_VALIDATE_ERROR
- PARAMETER_MISSING
- PARAMETER_VALIDATE_ERROR
AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14).
Data as JSON: /api/errors/5ba3bc48932253bf.
Report an issue: GitHub.