Tencent/WeKnora · error
failed to upload bytes to S3: %w
Error message
failed to upload bytes to S3: %w
What it means
SaveBytes wraps errors from the S3 PutObject upload of the byte payload. The reader and metadata were constructed but the upload itself failed — permissions, size/quota limits, connectivity, or encryption configuration. Nothing is persisted and no s3:// path is returned.
Source
Thrown at internal/application/service/file/s3.go:343
func (s *s3FileService) SaveBytes(ctx context.Context, data []byte, tenantID uint64, fileName string, temp bool) (string, error) {
safeName, err := utils.SafeFileName(fileName)
if err != nil {
return "", fmt.Errorf("invalid file name: %w", err)
}
ext := filepath.Ext(safeName)
objectName := fmt.Sprintf("%s%d/exports/%s%s", s.pathPrefix, tenantID, uuid.New().String(), ext)
// Upload bytes to S3
reader := bytes.NewReader(data)
_, err = s.client.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(s.bucketName),
Key: aws.String(objectName),
Body: reader,
ContentLength: aws.Int64(int64(len(data))),
ContentType: aws.String(utils.GetContentTypeByExt(ext)),
})
if err != nil {
return "", fmt.Errorf("failed to upload bytes to S3: %w", err)
}
return fmt.Sprintf("s3://%s/%s", s.bucketName, objectName), nil
}
// GetFileURL returns a presigned download URL for the file
func (s *s3FileService) GetFileURL(ctx context.Context, filePath string) (string, error) {
objectName, err := s.parseS3FilePath(filePath)
if err != nil {
return "", err
}
// Create presign client
presignClient := s3.NewPresignClient(s.client)
// Generate presigned URL
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
Bucket: aws.String(s.bucketName),View on GitHub (pinned to 988cbb0330)
Solutions
- Verify IAM s3:PutObject permission on pathPrefix/tenantID/exports/*
- Unwrap errors.As(smithy.APIError) to identify the exact S3 error code
- Check object size against bucket quotas and S3 limits, and confirm KMS key grants if SSE-KMS is enabled
- Confirm network/endpoint configuration and retry transient failures with backoff
Example fix
// before
path, err := svc.SaveBytes(ctx, data, tenantID, name, false)
// after
path, err := svc.SaveBytes(ctx, data, tenantID, name, false)
if err != nil {
var apiErr smithy.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "AccessDenied" {
return fmt.Errorf("check s3:PutObject on %s/exports/*: %w", tenantID, err)
}
return err
} Defensive patterns
Strategy: retry
Validate before calling
if len(data) == 0 { return errors.New("empty payload") }
if _, err := utils.SafeFileName(fileName); err != nil { return err } Try / catch
var path string
err := retry.Do(3, backoff, func() error {
var e error
path, e = svc.SaveBytes(ctx, data, tenantID, name, false)
var apiErr smithy.APIError
if e != nil && errors.As(e, &apiErr) && isTransient(apiErr.ErrorCode()) {
return e // retry
}
return retry.Stop(e)
}) Prevention
- Retry only transient S3 errors (5xx, throttling, timeouts), never AccessDenied
- Confirm IAM s3:PutObject on pathPrefix/tenantID/exports/* before rollout
- Check bucket quotas and KMS grants for large uploads
- Monitor S3 error rates and set size limits at the API layer
When it happens
Trigger: PutObject failing due to AccessDenied on the tenant prefix, payload exceeding bucket quota or max object size, KMS key access denied, or network failure to the S3 endpoint.
Common situations: IAM policy not granting PutObject on the exports/ prefix; large export files hitting bucket lifecycle/quota limits; SSE-KMS bucket where the role lacks kms:GenerateDataKey; endpoint unreachable from a private subnet.
Related errors
- failed to upload file to OBS: %w
- failed to get file from S3: %w
- failed to get file from OBS: %w
- failed to copy file in OBS: %w
- failed to upload bytes to OBS: %w
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/d0d6e37854edb0fe.
Report an issue: GitHub.