iflytek/astron-agent · error · RuntimeError
artifact_upload_failed
Error message
artifact_upload_failed
What it means
ArtifactUploader.upload POSTs a multipart form (flowId, uid, skillId, file, auth token header) to the artifact upload URL. Any failure — non-2xx status, redirect, invalid JSON, or a business envelope where code != 0 or data is not a dict — is re-raised as RuntimeError(ARTIFACT_UPLOAD_FAILED_ERROR) ('artifact_upload_failed').
Solutions
- Check the artifact upload service health and that artifact_upload_url points to the correct environment
- Verify the upload token (X-Skill-Sandbox-Artifact-Token) is present and not expired, and that workflow_id/uid in SkillSandboxConfig are set correctly
- Inspect the artifact size against server limits and compress or shrink it if rejected
- Retry the upload after a transient failure; capture the upstream status/body in server logs to pinpoint 4xx vs 5xx causes
Example fix
// before: token loaded but possibly empty
headers = {'X-Skill-Sandbox-Artifact-Token': artifact_upload_token}
// after
if not artifact_upload_token:
raise RuntimeError('artifact upload token is not configured')
headers = {'X-Skill-Sandbox-Artifact-Token': artifact_upload_token} Defensive patterns
Strategy: retry
Validate before calling
from uploader import ArtifactUploader
if not uploader.is_configured():
raise RuntimeError('artifact upload URL/token or workflow_id/uid not configured')
if len(file_bytes) > MAX_UPLOAD_BYTES:
raise ValueError('artifact exceeds upload size limit') Type guard
def upload_envelope_ok(payload) -> bool:
return (isinstance(payload, dict) and isinstance(payload.get('code'), int)
and not isinstance(payload.get('code'), bool)
and payload.get('code') == 0 and isinstance(payload.get('data'), dict)) Try / catch
for attempt in range(3):
try:
return await uploader.upload(name, data, ctype)
except RuntimeError:
if attempt == 2:
logger.error('artifact upload failed after retries')
raise
await asyncio.sleep(2 ** attempt) Prevention
- Monitor artifact upload service health and 5xx rates
- Rotate and distribute the upload token before expiry
- Verify artifact_upload_url per environment in deployment config
- Log upstream status/body on failure to distinguish auth vs size vs availability
When it happens
Trigger: Upload endpoint returns 4xx/5xx or a redirect (allow_redirects=False), response body is not valid JSON, business code is non-zero, data field missing, upload token missing/invalid, or a network timeout (60s total).
Common situations: Artifact upload service down or returning 502 behind a gateway; expired/rotated X-Skill-Sandbox-Artifact-Token; upload URL env var pointing at the wrong environment; artifact exceeding a server-side size limit; workflow_id/uid misconfigured so the backend rejects the flow.
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
- errorMessage (dynamic; error.message or fallback 'Failed to…
- sandbox-exec failed: HTTP
- Skill resource download failed: HTTP
- MODEL_CHECK_FAILED
- REPO_KNOWLEDGE_DOWNLOAD_FAILED
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/c65907fb11d890c3.
Report an issue: GitHub.
Appendix: source
Thrown at core/agent/service/plugin/skill_sandbox.py:874
headers=headers,
allow_redirects=False,
) as response:
if not 200 <= response.status < 300:
raise ValueError
response.raise_for_status()
payload = await response.json(content_type=None)
code = payload.get("code") if isinstance(payload, dict) else None
data = payload.get("data") if isinstance(payload, dict) else None
if (
isinstance(code, bool)
or not isinstance(code, int)
or code != 0
or not isinstance(data, dict)
):
raise ValueError
return data
except Exception:
raise RuntimeError(ARTIFACT_UPLOAD_FAILED_ERROR) from None
View on GitHub (pinned to 5e758547a8)