iflytek/astron-agent · error · CustomException

FILE_STORAGE_ERROR

FILE_STORAGE_ERROR

Error message

FILE_STORAGE_ERROR

What it means

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.

Solutions

  1. Read cause_error on the CustomException to see the exact botocore error (e.g. SignatureDoesNotMatch, AccessDenied, AccessControlListNotSupported).
  2. Verify OSS endpoint, access key, secret and bucket env values for the workflow service.
  3. Test connectivity from the service host: curl the MinIO/S3 endpoint endpoint_url.
  4. If using AWS S3 with ACLs disabled, remove the ACL="public-read" param and use a bucket policy for public reads instead.
  5. Check IAM/bucket policy allows s3:PutObject and s3:PutObjectAcl on the bucket.

Example fix

// before
self.client.put_object(Bucket=bucket_name, Key=filename, Body=file_bytes, ACL="public-read")
# AccessControlListNotSupported on ACL-disabled buckets
// after
self.client.put_object(Bucket=bucket_name, Key=filename, Body=file_bytes)  # public access via bucket policy
Defensive patterns

Strategy: try-catch

Validate before calling

def bucket_writable(client, bucket):
    try:
        client.head_bucket(Bucket=bucket)
        return True
    except ClientError:
        return False

Type guard

def oss_configured(cfg) -> bool:
    return bool(cfg.endpoint and cfg.access_key_id and cfg.access_key_secret and cfg.bucket_name)

Try / catch

try:
    url = s3_service.upload_file(filename, data)
except CustomException as e:
    logger.error(f"Upload failed: {e.cause_error}")
    raise

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/59f793794d066469. Report an issue: GitHub.

Appendix: source

Thrown at core/workflow/extensions/middleware/oss/manager.py:130

        Upload a file to S3-compatible storage with public read access.

        :param filename: The name of the file to be uploaded
        :param file_bytes: The binary content of the file to upload
        :param bucket_name: Optional bucket name, uses default if not provided
        :return: The public download URL for the uploaded file
        :raises CustomException: If file upload fails
        """
        if not bucket_name:
            bucket_name = self.bucket_name

        try:
            # Set public read access
            self.client.put_object(
                Bucket=bucket_name, Key=filename, Body=file_bytes, ACL="public-read"
            )
            return f"{self.oss_download_host}/{bucket_name}/{filename}"
        except Exception as e:
            raise CustomException(
                CodeEnum.FILE_STORAGE_ERROR, cause_error=str(e)
            ) from e

    async def upload_file_async(
        self, filename: str, file_bytes: bytes, bucket_name: Optional[str] = None
    ) -> str:
        """
        Upload a file to S3-compatible storage with public read access.

        :param filename: The name of the file to be uploaded
        :param file_bytes: The binary content of the file to upload
        :param bucket_name: Optional bucket name, uses default if not provided
        :return: The public download URL for the uploaded file
        :raises CustomException: If file upload fails
        """
        if not bucket_name:
            bucket_name = self.bucket_name

View on GitHub (pinned to 5e758547a8)