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

  1. Check the artifact upload service health and that artifact_upload_url points to the correct environment
  2. Verify the upload token (X-Skill-Sandbox-Artifact-Token) is present and not expired, and that workflow_id/uid in SkillSandboxConfig are set correctly
  3. Inspect the artifact size against server limits and compress or shrink it if rejected
  4. 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

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


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)