{"record":{"id":"5ba3bc48932253bf","repo":"alibaba/nacos","slug":"skill-cannot-be-null","errorCode":null,"errorMessage":"Skill cannot be null","messagePattern":"Skill cannot be null","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillUtils.java","lineNumber":112,"sourceCode":"        \n        return skill.getSkillMd() == null ? EMPTY_STRING : skill.getSkillMd();\n    }\n    \n    /**\n     * Convert Skill object to a ZIP byte array containing all skill files.\n     *\n     * <p>The ZIP structure mirrors the upload format:\n     * {@code skillName/SKILL.md}, {@code skillName/type/resourceName}, etc.\n     * Binary resources (marked with metadata encoding=base64) are decoded back to raw bytes.</p>\n     *\n     * @param skill the Skill object to convert\n     * @return ZIP file as byte array\n     * @throws IOException if ZIP creation fails\n     * @throws IllegalArgumentException if skill is null or skill name is blank\n     */\n    public static byte[] toZipBytes(Skill skill) throws IOException {\n        if (skill == null) {\n            throw new IllegalArgumentException(\"Skill cannot be null\");\n        }\n        if (StringUtils.isBlank(skill.getName())) {\n            throw new IllegalArgumentException(\"Skill name cannot be blank\");\n        }\n        \n        String skillName = skill.getName();\n        ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        try (ZipOutputStream zos = new ZipOutputStream(baos)) {\n            // 1. SKILL.md\n            zos.putNextEntry(new ZipEntry(skillName + \"/SKILL.md\"));\n            zos.write(toMarkdown(skill).getBytes(StandardCharsets.UTF_8));\n            zos.closeEntry();\n            \n            // 2. Resource files\n            if (skill.getResource() != null && !skill.getResource().isEmpty()) {\n                for (SkillResource resource : skill.getResource().values()) {\n                    if (resource == null || StringUtils.isBlank(resource.getName())) {\n                        continue;","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillUtils.java#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nbyte[] zip = SkillUtils.toZipBytes(skillService.find(name)); // may be null\n\n// after\nSkill skill = skillService.find(name);\nif (skill == null) {\n    throw new NoSuchElementException(\"Skill not found: \" + name);\n}\nbyte[] zip = SkillUtils.toZipBytes(skill);","handlingStrategy":"validation","validationCode":"if (skill == null) {\n    throw new NoSuchElementException(\"Skill not found\");\n}\nbyte[] zip = SkillUtils.toZipBytes(skill);","typeGuard":"public static boolean isSkillReadyForZip(Skill skill) {\n    return skill != null && skill.getName() != null && !skill.getName().trim().isEmpty();\n}","tryCatchPattern":"try {\n    byte[] zip = SkillUtils.toZipBytes(skill);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"Skill cannot be null\")) {\n        return notFound(\"Skill not found\");\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["skill","validation","non-null","zip"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}