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