{"record":{"id":"dcefba4dfa3eec52","repo":"iflytek/astron-agent","slug":"skill-resource-download-failed-http","errorCode":null,"errorMessage":"Skill resource download failed: HTTP ","messagePattern":"Skill resource download failed: HTTP ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/springai/SkillRuntimeToolService.java","lineNumber":123,"sourceCode":"\n    /** Download a text resource (SKILL.md or a referenced file) from a presigned URL. */\n    public String downloadText(String url) throws IOException {\n        validateLimit(maxResourceBytes, \"Skill resource\");\n        validateResourceUrl(url);\n        Request request;\n        try {\n            request = new Request.Builder().url(url).get().build();\n        } catch (IllegalArgumentException exception) {\n            throw new IOException(\"Skill resource URL is not allowed\");\n        }\n        OkHttpClient client = httpClient.newBuilder()\n                .callTimeout(Duration.ofSeconds(30))\n                .followRedirects(false)\n                .followSslRedirects(false)\n                .build();\n        try (Response response = client.newCall(request).execute()) {\n            if (!response.isSuccessful()) {\n                throw new IOException(\"Skill resource download failed: HTTP \" + response.code());\n            }\n            ResponseBody body = response.body();\n            if (body == null) {\n                throw new IOException(\"Skill resource download returned empty body\");\n            }\n            return decodeText(readBounded(body, maxResourceBytes, \"Skill resource\"), body);\n        }\n    }\n\n    private byte[] readBounded(ResponseBody body, long limit, String description)\n            throws IOException {\n        validateLimit(limit, description);\n        long contentLength = body.contentLength();\n        if (contentLength > limit) {\n            throw new IOException(description + \" exceeds size limit\");\n        }\n        int readLimit = Math.toIntExact(limit + 1);\n        try (InputStream input = body.byteStream()) {","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/backend/hub/src/main/java/com/iflytek/astron/console/hub/service/chat/springai/SkillRuntimeToolService.java#L105-L141","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nString content = service.downloadText(staleUrl); // 404 after skill re-publish\n// after\n// ensure manifest references fresh artifact and handle failure\ntry {\n    String content = service.downloadText(currentUrl);\n} catch (IOException e) {\n    log.warn(\"skill resource unavailable: {}\", e.getMessage());\n    throw new BusinessException(ResponseEnum.SKILL_RESOURCE_UNAVAILABLE);\n}","handlingStrategy":"try-catch","validationCode":"boolean resourceReachable = httpHead(url).code() == 200;\nif (!resourceReachable) {\n    throw new IllegalStateException(\"Skill resource not reachable: \" + url);\n}","typeGuard":null,"tryCatchPattern":"try {\n    String content = service.downloadText(url, limit);\n} catch (IOException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Skill resource download failed: HTTP\")) {\n        int status = Integer.parseInt(e.getMessage().substring(e.getMessage().lastIndexOf(' ') + 1));\n        if (status >= 500) { /* retry with backoff */ } else { /* re-publish or fail fast */ }\n    }\n}","preventionTips":["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."],"tags":["http","network","java","download","okhttp"],"backgroundTag":"http-error-response","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}