{"record":{"id":"59f793794d066469","repo":"iflytek/astron-agent","slug":"file-storage-error","errorCode":"FILE_STORAGE_ERROR","errorMessage":"FILE_STORAGE_ERROR","messagePattern":"FILE_STORAGE_ERROR","errorType":"error_code","errorClass":"CustomException","httpStatus":null,"severity":"error","filePath":"core/workflow/extensions/middleware/oss/manager.py","lineNumber":130,"sourceCode":"        Upload a file to S3-compatible storage with public read access.\n\n        :param filename: The name of the file to be uploaded\n        :param file_bytes: The binary content of the file to upload\n        :param bucket_name: Optional bucket name, uses default if not provided\n        :return: The public download URL for the uploaded file\n        :raises CustomException: If file upload fails\n        \"\"\"\n        if not bucket_name:\n            bucket_name = self.bucket_name\n\n        try:\n            # Set public read access\n            self.client.put_object(\n                Bucket=bucket_name, Key=filename, Body=file_bytes, ACL=\"public-read\"\n            )\n            return f\"{self.oss_download_host}/{bucket_name}/{filename}\"\n        except Exception as e:\n            raise CustomException(\n                CodeEnum.FILE_STORAGE_ERROR, cause_error=str(e)\n            ) from e\n\n    async def upload_file_async(\n        self, filename: str, file_bytes: bytes, bucket_name: Optional[str] = None\n    ) -> str:\n        \"\"\"\n        Upload a file to S3-compatible storage with public read access.\n\n        :param filename: The name of the file to be uploaded\n        :param file_bytes: The binary content of the file to upload\n        :param bucket_name: Optional bucket name, uses default if not provided\n        :return: The public download URL for the uploaded file\n        :raises CustomException: If file upload fails\n        \"\"\"\n        if not bucket_name:\n            bucket_name = self.bucket_name\n","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/workflow/extensions/middleware/oss/manager.py#L112-L148","documentation":"S3Service.upload_file wraps any exception from the boto3 put_object call (uploading file_bytes to the bucket with public-read ACL) in a CustomException with code FILE_STORAGE_ERROR. It indicates the synchronous object-storage upload failed for any reason: credentials, network, bucket, or ACL permissions.","triggerScenarios":"put_object raises: invalid/expired S3 credentials, unreachable endpoint, non-existent or unauthorized bucket, object key violations, or a denied public-read ACL (bucket forbids ACLs, e.g. BucketOwnerEnforced ownership).","commonSituations":"Wrong OSS_* env config (endpoint/keys/bucket) for the environment; MinIO endpoint down; AWS S3 buckets created with ACLs disabled cause AccessControlListNotSupported; network egress blocked from the workflow pod.","solutions":["Read cause_error on the CustomException to see the exact botocore error (e.g. SignatureDoesNotMatch, AccessDenied, AccessControlListNotSupported).","Verify OSS endpoint, access key, secret and bucket env values for the workflow service.","Test connectivity from the service host: curl the MinIO/S3 endpoint endpoint_url.","If using AWS S3 with ACLs disabled, remove the ACL=\"public-read\" param and use a bucket policy for public reads instead.","Check IAM/bucket policy allows s3:PutObject and s3:PutObjectAcl on the bucket."],"exampleFix":"// before\nself.client.put_object(Bucket=bucket_name, Key=filename, Body=file_bytes, ACL=\"public-read\")\n# AccessControlListNotSupported on ACL-disabled buckets\n// after\nself.client.put_object(Bucket=bucket_name, Key=filename, Body=file_bytes)  # public access via bucket policy","handlingStrategy":"try-catch","validationCode":"def bucket_writable(client, bucket):\n    try:\n        client.head_bucket(Bucket=bucket)\n        return True\n    except ClientError:\n        return False","typeGuard":"def oss_configured(cfg) -> bool:\n    return bool(cfg.endpoint and cfg.access_key_id and cfg.access_key_secret and cfg.bucket_name)","tryCatchPattern":"try:\n    url = s3_service.upload_file(filename, data)\nexcept CustomException as e:\n    logger.error(f\"Upload failed: {e.cause_error}\")\n    raise","preventionTips":["Validate endpoint/keys/bucket config at startup with a head_bucket probe.","Match IAM/bucket policy to put_object + public-read ACL requirements.","If the bucket has ACLs disabled (BucketOwnerEnforced), grant public read via bucket policy instead of ACL.","Check egress/network from the workflow pod to the S3/MinIO endpoint before rollout.","Retry transient 5xx/network errors with backoff before surfacing to users."],"tags":["s3","oss","file-upload","boto3","storage"],"backgroundTag":"file-write-failed","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}