alibaba/nacos · error · NacosException

SERVER_ERROR

SERVER_ERROR

Error message

Failed to create skill zip: ${e.getMessage()}

What it means

Thrown by SkillRequestUtil.buildSkillZipResponse when SkillUtils.toZipBytes(skill) fails during a skill export/download operation. The method serializes a Skill domain object into ZIP bytes and builds a ResponseEntity with application/zip content type and Content-Disposition headers. Any exception — IO failure during ZIP streaming, null fields on the Skill object, or serialization issues — is caught and re-wrapped as NacosException with SERVER_ERROR (500).

Source

Thrown at ai/src/main/java/com/alibaba/nacos/ai/utils/SkillRequestUtil.java:70

    
    /**
     * Build a ZIP download {@link ResponseEntity} from a {@link Skill} object.
     *
     * <p>Shared by all controllers that need to export a skill as ZIP.</p>
     *
     * @param skill the Skill object
     * @return ResponseEntity containing ZIP bytes with proper headers
     * @throws NacosException if ZIP creation fails
     */
    public static ResponseEntity<byte[]> buildSkillZipResponse(Skill skill) throws NacosException {
        try {
            byte[] zipBytes = SkillUtils.toZipBytes(skill);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.parseMediaType("application/zip"));
            headers.add("Content-Disposition", "attachment;filename=" + skill.getName() + ".zip");
            return new ResponseEntity<>(zipBytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            throw new NacosException(NacosException.SERVER_ERROR,
                "Failed to create skill zip: " + e.getMessage(), e);
        }
    }
    
    /**
     * Build a ZIP download {@link ResponseEntity} together with the listener-related headers
     * ({@code ETag}, {@code X-Nacos-Skill-Md5} and {@code X-Nacos-Skill-Resolved-Version}).
     *
     * <p>{@code md5} and {@code resolvedVersion} may be blank; only non-blank values are emitted as
     * headers so legacy paths that do not yet carry MD5 keep their existing response shape.
     *
     * @param skill           the Skill object
     * @param md5             published content MD5, optional
     * @param resolvedVersion resolved version when caller queries by label, optional
     * @return ResponseEntity containing ZIP bytes with proper headers
     * @throws NacosException if ZIP creation fails
     */
    public static ResponseEntity<byte[]> buildSkillZipResponseWithMd5(Skill skill, String md5,

View on GitHub (pinned to 9b989acdf1)

Solutions

  1. Check server logs for the chained cause exception (e) to identify whether the failure is a null field, IO error, or serialization issue.
  2. Verify the Skill object retrieved from storage is complete — non-null name, description, skillMd, and resource map — before the export endpoint is reached.
  3. If the skill was recently migrated or imported, re-import it from a known-good ZIP to ensure all fields are populated.
  4. File a bug if the cause is a null field in a Skill that should always be complete — the export path should validate before attempting serialization.

Example fix

// before — buildSkillZipResponse throws SERVER_ERROR on null skill.getName()
// (no caller-side fix; this is a server-side integrity issue)

// after — guard in the controller before calling buildSkillZipResponse
if (skill == null || StringUtils.isBlank(skill.getName()) || StringUtils.isBlank(skill.getSkillMd())) {
    throw new NacosApiException(NacosApiException.INVALID_PARAM,
        ErrorCode.DATA_EMPTY, "Skill data is incomplete and cannot be exported");
}
return SkillRequestUtil.buildSkillZipResponse(skill);
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate skill completeness before calling buildSkillZipResponse
if (skill == null || StringUtils.isBlank(skill.getName())
    || StringUtils.isBlank(skill.getSkillMd())) {
    throw new IllegalStateException("Skill is incomplete; cannot export as ZIP");
}

Type guard

public static boolean isExportableSkill(Skill skill) {
    return skill != null
        && StringUtils.isNotBlank(skill.getName())
        && StringUtils.isNotBlank(skill.getSkillMd());
}

Try / catch

try {
    return SkillRequestUtil.buildSkillZipResponse(skill);
} catch (NacosException e) {
    if (e.getErrCode() == NacosException.SERVER_ERROR) {
        log.error("Skill ZIP export failed for skill [{}]", skill.getName(), e);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling GET on a skill export/download endpoint (admin or console) where the underlying Skill object retrieved from storage has corrupted or null fields (e.g. null name used in Content-Disposition header filename, null skillMd content), or where SkillUtils.toZipBytes encounters an I/O error writing the archive to its ByteArrayOutputStream.

Common situations: Skill was partially written to storage with a null skillMd or null resource map; a storage migration left a Skill record in an inconsistent state; concurrent deletion removed the skill between the fetch and the ZIP build; a custom SkillUtils or SkillResource subclass throws during serialization.

Related errors


AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14). Data as JSON: /api/errors/414396582f799162. Report an issue: GitHub.