iflytek/astron-agent · error · IOException

Skill resource URL is not allowed

Error message

Skill resource URL is not allowed

What it means

downloadText first validates the skill resource URL (validateResourceUrl, origin/allow-list enforcement) and then builds the OkHttp request. If Request.Builder().url(url) throws IllegalArgumentException (malformed URL), it is converted to an IOException "Skill resource URL is not allowed". The message deliberately does not distinguish malformed URL from policy-rejected so callers learn nothing about the validation internals.

Solutions

  1. Check the URL is absolute, well-formed, and uses the allowed scheme/host required by validateResourceUrl.
  2. Point the skill resource at the approved same-origin host (e.g. the skill repository service) instead of a foreign origin.
  3. Validate/normalize URLs in the skill manifest at publish time so bad URLs never reach runtime.
  4. If a legitimate host is blocked, add it to the configured allow-list — do not bypass the validation.

Example fix

// before
String text = service.downloadText("skills.example.com/file.txt"); // no scheme -> IllegalArgumentException -> IOException
// after
URL u = new URI("https://skills.internal.example.com/file.txt").toURL(); // absolute, allow-listed origin
if (u.getHost().endsWith(".internal.example.com")) {
    String text = service.downloadText(u.toString());
}
Defensive patterns

Strategy: validation

Validate before calling

boolean isAllowedResourceUrl(String url) {
    try {
        URI u = new URI(url);
        String scheme = u.getScheme();
        String host = u.getHost();
        return ("https".equals(scheme) || "http".equals(scheme))
                && host != null && host.endsWith(".internal.example.com");
    } catch (URISyntaxException e) {
        return false;
    }
}

Type guard

boolean isAbsoluteHttpUrl(String url) {
    try {
        URI u = new URI(url);
        return u.isAbsolute() && u.getHost() != null;
    } catch (URISyntaxException e) {
        return false;
    }
}

Try / catch

try {
    String content = service.downloadText(url, limit);
} catch (IOException e) {
    if ("Skill resource URL is not allowed".equals(e.getMessage())) {
        // reject the skill resource: malformed or non-allow-listed origin
    }
}

Prevention

When it happens

Trigger: Calling downloadText (via skill resource read) with a URL that fails validateResourceUrl (foreign origin, non-allow-listed host, disallowed scheme) or with a syntactically invalid URL string that OkHttp cannot parse.

Common situations: A skill's resource reference points to an external host not on the allow-list (SSRF protection); a skill manifest contains a relative or malformed URL; the URL was attacker-controlled and got rejected by design; scheme is ftp:// or file:// instead of http(s).

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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

Appendix: source

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

            }
            ResponseBody respBody = response.body();
            return respBody == null
                    ? ""
                    : decodeText(
                            readBounded(respBody, maxSandboxResponseBytes, "Sandbox response"),
                            respBody);
        }
    }

    /** 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);
        }
    }

View on GitHub (pinned to 5e758547a8)