conductor-oss/conductor · critical · IllegalStateException

Failed to read skill file: {e.getMessage()}

Error message

Failed to read skill file: {e.getMessage()}

What it means

Thrown by readFile() as an IllegalStateException when an IOException occurs while streaming the skill's zip package. Unlike the IllegalArgumentExceptions around it, this indicates an unexpected I/O failure (e.g. corrupt or truncated zip data read back from the package store, stream closed mid-read) rather than a caller input mistake. The original IOException is attached as the cause.

Source

Thrown at agentspan/src/main/java/org/conductoross/conductor/ai/agentspan/runtime/service/SkillRegistryService.java:299

                }
                byte[] data =
                        readBounded(
                                zip,
                                maxPreviewBytes,
                                "Skill file is too large to preview: " + cleanPath);
                boolean binary = isBinary(cleanPath, data);
                return SkillFileContent.builder()
                        .path(cleanPath)
                        .contentType(contentType(cleanPath))
                        .size(data.length)
                        .binary(binary)
                        .content(binary ? null : new String(data, StandardCharsets.UTF_8))
                        .contentBase64(binary ? Base64.getEncoder().encodeToString(data) : null)
                        .build();
            }
            throw new IllegalArgumentException("Skill file not found: " + cleanPath);
        } catch (IOException e) {
            throw new IllegalStateException("Failed to read skill file: " + e.getMessage(), e);
        }
    }

    @SuppressWarnings("unchecked")
    public Map<String, Object> resolveRawConfig(Map<String, Object> skillRef) {
        requireSkillStorage();
        if (skillRef == null) {
            throw new IllegalArgumentException("skillRef is required");
        }
        String name = requiredString(skillRef, "name");
        String version = stringValue(skillRef.get("version"));
        SkillDetail detail = get(name, version);
        Map<String, Object> rawConfig = rawConfigForDetail(detail, new HashSet<>());
        Object model = skillRef.get("model");
        if (model instanceof String s && !s.isBlank()) {
            rawConfig.put("model", s);
        }
        Object agentModels = skillRef.get("agentModels");

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Re-register the skill package so a fresh, intact zip is stored (DELETE then POST /register, or POST with the same checksum to restore).
  2. Check the package store / storage backend health and the stored handle's integrity.
  3. Inspect the caused-by IOException message for the specific read failure location.

Example fix

// no code fix — recover by re-uploading the package
DELETE /api/skills/foo/versions/1.0.0
POST /api/skills/register  (manifest + fresh package zip)
Defensive patterns

Strategy: try-catch

Try / catch

try { return skillRegistryService.readFile(name, version, path); }
catch (IllegalStateException e) {
    if (e.getMessage().startsWith("Failed to read skill file")) {
        // package storage corrupt: re-register the package, then retry
        log.error("skill file read failed; package may be corrupt", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: GET /api/skills/{name}/versions/{version}/files?path=SKILL.md when the stored package bytes are corrupt or partially written, or when the underlying packageStore.read() returns truncated data that breaks ZipInputStream.

Common situations: Package store corruption after an interrupted write; a backend restart mid-store; filesystem or object-storage returning partial bytes; a zip that was never validly closed at upload time.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/085d71e1d456f77f. Report an issue: GitHub.