{"record":{"id":"f1dcd7e6d1188cce","repo":"iflytek/astron-agent","slug":"remote-resource-returned-http-response-status","errorCode":null,"errorMessage":"Remote resource returned HTTP {response.status}","messagePattern":"Remote resource returned HTTP (.+?)","errorType":"exception","errorClass":"RemoteResourcePolicyError","httpStatus":null,"severity":"error","filePath":"core/plugin/aitools/common/clients/safe_download.py","lineNumber":140,"sourceCode":"            CodeEnums.HTTPClientError,\n            extra_message=\"Remote resource download failed\",\n        ) from exc\n\n\nasync def _download_resource(\n    url: str,\n    connector: aiohttp.TCPConnector,\n    timeout: aiohttp.ClientTimeout,\n    max_bytes: int,\n) -> bytes:\n    async with aiohttp.ClientSession(\n        connector=connector,\n        timeout=timeout,\n        trust_env=False,\n    ) as session:\n        async with session.get(url, allow_redirects=False) as response:\n            if not 200 <= response.status < 300:\n                raise RemoteResourcePolicyError(\n                    f\"Remote resource returned HTTP {response.status}\"\n                )\n            return await _read_bounded_response(response, max_bytes)\n\n\nasync def _read_bounded_response(\n    response: aiohttp.ClientResponse,\n    max_bytes: int,\n) -> bytes:\n    content_length = response.content_length\n    if content_length is not None and content_length > max_bytes:\n        raise RemoteResourcePolicyError(\"Remote resource is too large\")\n\n    content = bytearray()\n    async for chunk in response.content.iter_chunked(_DOWNLOAD_CHUNK_SIZE):\n        if len(content) + len(chunk) > max_bytes:\n            raise RemoteResourcePolicyError(\"Remote resource is too large\")\n        content.extend(chunk)","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/plugin/aitools/common/clients/safe_download.py#L122-L158","documentation":"In _download_resource, any response status outside 200–299 (redirects are NOT followed, allow_redirects=False) raises RemoteResourcePolicyError with the status code embedded in the message. This converts HTTP-level failures and redirects into policy rejections so callers cannot be tricked into following redirects to internal hosts.","triggerScenarios":"The remote server replies 3xx (301/302/307 redirect), 403 (private object), 404 (missing file), 5xx, etc. on a GET of the caller-supplied URL.","commonSituations":"Object storage returning 302 to a presigned CDN URL; expired presigned S3 URL giving 403; typo'd object key giving 404; rate-limited or erroring upstream giving 5xx.","solutions":["Read the HTTP status from the message and address it: fix the object key for 404, refresh presigned credentials for 403.","Resolve the redirect yourself: fetch the final public http(s) URL and pass that directly — the library will not follow redirects by design (SSRF protection).","Retry only on transient 5xx, ideally with backoff; do not retry 4xx."],"exampleFix":"// before\nawait fetch_public_resource(\"https://cdn.example.com/file\")  // server 302s elsewhere\n// after\nawait fetch_public_resource(\"https://origin.example.com/actual/file\")  // final, non-redirecting URL","handlingStrategy":"validation","validationCode":"# resolve redirects yourself before calling\nimport aiohttp\nasync def final_url(url):\n    async with aiohttp.ClientSession() as s:\n        async with s.head(url, allow_redirects=True) as r:\n            return str(r.url)","typeGuard":null,"tryCatchPattern":"try:\n    data = await fetch_public_resource(url)\nexcept HTTPClientException as e:\n    if \"HTTP 3\" in str(e):\n        url = await resolve_redirect(url); data = await fetch_public_resource(url)","preventionTips":["Use direct, non-redirecting object URLs","Refresh presigned URLs before they expire (403)","Verify object keys exist (404) before download"],"tags":["http","network","redirect","ssrf","python"],"backgroundTag":"http-error-response","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}