iflytek/astron-agent · error · IOException

Skill resource download failed: HTTP

Error message

Skill resource download failed: HTTP 

What it means

downloadText fetches a skill resource over HTTP with redirects disabled and a 30s call timeout. If the remote server responds with a non-2xx status, it throws "Skill resource download failed: HTTP <code>". Only the status code is exposed, never the response body or URL, to avoid leaking request details.

Solutions

  1. Confirm the resource URL is still valid and published (fetch it manually from the backend network).
  2. Re-upload or re-publish the skill so its resource references point to current artifacts.
  3. Check whether the resource host requires authentication and supply/refresh credentials in the request.
  4. Retry on transient 5xx; investigate registry/CDN health if 5xx persists.

Example fix

// before
String content = service.downloadText(staleUrl); // 404 after skill re-publish
// after
// ensure manifest references fresh artifact and handle failure
try {
    String content = service.downloadText(currentUrl);
} catch (IOException e) {
    log.warn("skill resource unavailable: {}", e.getMessage());
    throw new BusinessException(ResponseEnum.SKILL_RESOURCE_UNAVAILABLE);
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean resourceReachable = httpHead(url).code() == 200;
if (!resourceReachable) {
    throw new IllegalStateException("Skill resource not reachable: " + url);
}

Try / catch

try {
    String content = service.downloadText(url, limit);
} catch (IOException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Skill resource download failed: HTTP")) {
        int status = Integer.parseInt(e.getMessage().substring(e.getMessage().lastIndexOf(' ') + 1));
        if (status >= 500) { /* retry with backoff */ } else { /* re-publish or fail fast */ }
    }
}

Prevention

When it happens

Trigger: The skill resource server returns 404 (resource moved/deleted), 401/403 (auth required), 410 (expired artifact), or 5xx while downloadText executes during a skill read.

Common situations: A skill's bundled resource URL became stale after a version upgrade or registry cleanup; the resource requires a token that the console backend does not send; a CDN/gateway returns 403 for the internal caller; transient 502/503 during registry redeploy.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/dcefba4dfa3eec52. Report an issue: GitHub.

Appendix: source

Thrown at console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/springai/SkillRuntimeToolService.java:123

    /** Download a text resource (SKILL.md or a referenced file) from a presigned URL. */
    public String downloadText(String url) throws IOException {
        validateLimit(maxResourceBytes, "Skill resource");
        validateResourceUrl(url);
        Request request;
        try {
            request = new Request.Builder().url(url).get().build();
        } catch (IllegalArgumentException exception) {
            throw new IOException("Skill resource URL is not allowed");
        }
        OkHttpClient client = httpClient.newBuilder()
                .callTimeout(Duration.ofSeconds(30))
                .followRedirects(false)
                .followSslRedirects(false)
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Skill resource download failed: HTTP " + response.code());
            }
            ResponseBody body = response.body();
            if (body == null) {
                throw new IOException("Skill resource download returned empty body");
            }
            return decodeText(readBounded(body, maxResourceBytes, "Skill resource"), body);
        }
    }

    private byte[] readBounded(ResponseBody body, long limit, String description)
            throws IOException {
        validateLimit(limit, description);
        long contentLength = body.contentLength();
        if (contentLength > limit) {
            throw new IOException(description + " exceeds size limit");
        }
        int readLimit = Math.toIntExact(limit + 1);
        try (InputStream input = body.byteStream()) {

View on GitHub (pinned to 5e758547a8)