iflytek/astron-agent · warning · RuntimeError
Skill resource is unavailable
Error message
Skill resource is unavailable
What it means
RuntimeError(SKILL_RESOURCE_ERROR) ('Skill resource is unavailable') raised in SkillPlugin._download_text (skill.py:250). The skill resource was downloaded within the byte limit, but its bytes are not valid UTF-8, so decoding with errors='strict' raises UnicodeDecodeError, which is converted into this user-facing error. It protects downstream consumers (LLM prompts, sandbox) from binary garbage masquerading as skill text.
Solutions
- Re-upload the skill resource as a UTF-8 text file (convert encoding, e.g. iconv -f GBK -t UTF-8).
- Verify the download_url actually points to the text file and not a binary or HTML error page; fetch it and run `file` on it.
- Only mark text-decodable files as downloadable skill resources; exclude binaries at skill-publish time.
- If the content is legitimately non-text, serve it as a sandbox file resource rather than inline skill text.
Example fix
// before
value = await download_skill_resource(session, url, MAX_SKILL_TEXT_BYTES)
return value.decode("utf-8", errors="strict")
// after: tolerate BOM / pick a fallback before failing
value = await download_skill_resource(session, url, MAX_SKILL_TEXT_BYTES)
if value.startswith(codecs.BOM_UTF8):
value = value[len(codecs.BOM_UTF8):]
try:
return value.decode("utf-8")
except UnicodeDecodeError:
return value.decode("utf-8", errors="replace") Defensive patterns
Strategy: validation
Validate before calling
def is_valid_utf8(data: bytes) -> bool:
try:
data.decode("utf-8")
return True
except UnicodeDecodeError:
return False
# call before treating the download as skill text
if not is_valid_utf8(value):
raise RuntimeError(SKILL_RESOURCE_ERROR) Type guard
def looks_like_text(data: bytes) -> bool:
text_chars = bytes(range(32, 127)) + b"\n\r\t"
return not bool(data.translate(None, delete=text_chars)) Try / catch
try:
text = await plugin._download_text(url)
except RuntimeError as e:
if str(e) == SKILL_RESOURCE_ERROR:
logger.warning("skill resource %s is not valid UTF-8 text; skipping", url)
raise Prevention
- Publish skill resources only as UTF-8-encoded text files
- Reject binary uploads at skill-publish time using a magic-byte check
- Verify download_urls resolve to the actual text file, not redirects or error pages
- Strip BOM and normalize encodings when importing skills
When it happens
Trigger: Downloading a skill resource whose download_url points to a binary file (zip, pdf, image, or non-UTF-8 encoded text) instead of UTF-8 text, then attempting decode.
Common situations: Skill author uploaded a binary asset or a file saved with a non-UTF-8 encoding (GBK, UTF-16, Latin-1), or the URL redirects to an error page/CDN binary response instead of the text resource.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Remote resource path is invalid
- read_failed
- model.encryptionFailed
- Skill resource download failed: HTTP
- Skill resource download returned empty body
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/775bd37119584324.
Report an issue: GitHub.
Appendix: source
Thrown at core/agent/service/plugin/skill.py:250
}
return PluginResponse(result=result)
return _runner
def _normalize_path(self, value: Any) -> str:
path = str(value or "").strip().replace("\\", "/")
while path.startswith("./"):
path = path[2:]
return path.lstrip("/")
async def _download_text(self, url: str) -> str:
timeout = aiohttp.ClientTimeout(total=30)
async with aiohttp.ClientSession(timeout=timeout) as session:
value = await download_skill_resource(session, url, MAX_SKILL_TEXT_BYTES)
try:
return value.decode("utf-8", errors="strict")
except UnicodeDecodeError:
raise RuntimeError(SKILL_RESOURCE_ERROR) from None
View on GitHub (pinned to 5e758547a8)