iflytek/astron-agent · error · RuntimeError
Artifact upload credential is missing or invalid
Error message
Artifact upload credential is missing or invalid
What it means
The skill sandbox artifact-upload feature requires an internal upload credential at least MIN_ARTIFACT_UPLOAD_TOKEN_LENGTH (32) chars long with no CR/LF. _load_artifact_upload_token reads it from SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN, or from the file named by SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN_FILE. If neither is set, the token file is unreadable, or the resulting token is too short or contains newline characters, upload() raises this RuntimeError.
Solutions
- Set SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN to a token of at least 32 characters with no whitespace/newlines
- Or set SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN_FILE to a readable file containing only the token (single line, UTF-8)
- Check the token file is mounted/accessible inside the container (ls/cat it in the pod) and has correct permissions
- Regenerate/reissue the credential if it is shorter than 32 characters or contains embedded newlines
Example fix
// before # env has no SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN // after export SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN="$(openssl rand -hex 32)" # or export SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN_FILE=/etc/secrets/artifact_upload_token
Defensive patterns
Strategy: validation
Validate before calling
import os
from pathlib import Path
def artifact_upload_credential_ready() -> bool:
token = (os.getenv("SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN") or "").strip()
if not token:
tf = (os.getenv("SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN_FILE") or "").strip()
if not tf:
return False
try:
token = Path(tf).read_text(encoding="utf-8").strip()
except (OSError, UnicodeError):
return False
return len(token) >= 32 and "\r" not in token and "\n" not in token Type guard
def is_valid_artifact_token(token: object) -> bool:
return (
isinstance(token, str)
and len(token) >= 32
and "\r" not in token
and "\n" not in token
) Try / catch
try:
await sandbox.upload(...)
except RuntimeError as exc:
if "Artifact upload credential" in str(exc):
raise ConfigError(
"SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN (or *_FILE) missing/invalid; "
"need >=32 chars, no newlines"
) from exc
raise Prevention
- Provision the credential via a mounted secret file in K8s rather than a bare env var
- Assert required SKILL_SANDBOX_* env vars at service startup with a fail-fast config check
- Generate tokens with `openssl rand -hex 32` (64 chars, no whitespace)
- Check for None/empty token in deployment smoke tests before enabling artifact upload
When it happens
Trigger: upload() called when SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN is unset/empty and SKILL_SANDBOX_ARTIFACT_UPLOAD_TOKEN_FILE is unset; Path.read_text on the token file raises OSError (missing file, permissions) or UnicodeError (invalid UTF-8) — line 168; or token length < 32 or contains \r/\n — line 169-170.
Common situations: Deployment env vars missing because the sandbox feature was never configured by an admin; the token file path points to a Kubernetes secret not mounted in the agent pod; a truncated or copy-pasted token shorter than 32 chars; a token file saved with a trailing newline inside quotes or Windows CRLF content.
Related errors
- Artifact upload configuration is unavailable
- 40028
- Sandbox runtime configuration is unavailable
- Sandbox initialization failed
- RAGFLOW_API_TOKEN not configured in environment variables
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/a96ab812fd480aeb.
Report an issue: GitHub.
Appendix: source
Thrown at core/agent/service/plugin/skill_sandbox.py:168
pass
print('{"error":"snapshot_failed"}')
finally:
if source_fd is not None:
os.close(source_fd)
if destination_fd is not None:
os.close(destination_fd)
""".strip()
def _load_artifact_upload_token() -> str:
token = (os.getenv(ARTIFACT_UPLOAD_TOKEN_ENV) or "").strip()
if not token:
token_file = (os.getenv(ARTIFACT_UPLOAD_TOKEN_FILE_ENV) or "").strip()
if token_file:
try:
token = Path(token_file).read_text(encoding="utf-8").strip()
except (OSError, UnicodeError) as exc:
raise RuntimeError(ARTIFACT_UPLOAD_CREDENTIAL_ERROR) from exc
if len(token) < MIN_ARTIFACT_UPLOAD_TOKEN_LENGTH or "\r" in token or "\n" in token:
raise RuntimeError(ARTIFACT_UPLOAD_CREDENTIAL_ERROR)
return token
def _load_artifact_upload_url() -> str:
artifact_upload_url = (os.getenv(ARTIFACT_UPLOAD_URL_ENV) or "").strip()
try:
parsed = urlsplit(artifact_upload_url)
if (
not artifact_upload_url
or len(artifact_upload_url) > 2048
or any(char in artifact_upload_url for char in ("\r", "\n", "\t"))
or parsed.scheme not in {"http", "https"}
or not parsed.hostname
or parsed.username is not None
or parsed.password is not None
or parsed.path != ARTIFACT_UPLOAD_PATHView on GitHub (pinned to 5e758547a8)