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
- Confirm the resource URL is still valid and published (fetch it manually from the backend network).
- Re-upload or re-publish the skill so its resource references point to current artifacts.
- Check whether the resource host requires authentication and supply/refresh credentials in the request.
- 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
- Re-verify resource URLs after every skill re-publish or registry migration.
- Prefer stable versioned artifact URLs over mutable latest pointers.
- Monitor resource-host availability; retry only transient 5xx statuses.
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
- sandbox-exec failed: HTTP
- REPO_KNOWLEDGE_DOWNLOAD_FAILED
- Skill resource download returned empty body
- exceeds size limit
- Workflow chat HTTP " + response.code()
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)