Tencent/WeKnora · error
failed to copy file in S3: %w
Error message
failed to copy file in S3: %w
What it means
CopyFile wraps errors from the S3 CopyObject API call. The source key parsed successfully but the server-side copy failed — commonly missing s3:GetObject on the source, missing s3:PutObject on the destination, or object >5GB requiring multipart copy. The S3 error is chained via %w.
Source
Thrown at internal/application/service/file/s3.go:315
) (string, error) {
srcKey, err := s.parseS3FilePath(srcPath)
if err != nil {
return "", fmt.Errorf("s3 copy rejected source %q: %w", srcPath, ErrCrossBackendCopy)
}
ext := filepath.Ext(srcPath)
destKey := fmt.Sprintf("%s%d/%s/%s%s", s.pathPrefix, tenantID, knowledgeID, uuid.New().String(), ext)
// CopySource is "bucket/key"; the '/' separators must NOT be percent-encoded
// (url.PathEscape would turn them into %2F and break the bucket/key split).
// srcKey is already validated by parseS3FilePath -> SafeObjectKey.
_, err = s.client.CopyObject(ctx, &s3.CopyObjectInput{
Bucket: aws.String(s.bucketName),
CopySource: aws.String(s.bucketName + "/" + srcKey),
Key: aws.String(destKey),
})
if err != nil {
return "", fmt.Errorf("failed to copy file in S3: %w", err)
}
newPath := fmt.Sprintf("s3://%s/%s", s.bucketName, destKey)
logger.Infof(ctx, "Copied S3 object %s to %s", srcPath, newPath)
return newPath, nil
}
// SaveBytes saves bytes data to S3 and returns the file path
// temp parameter is ignored for S3 (no auto-expiration support in this implementation)
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 S3View on GitHub (pinned to 988cbb0330)
Solutions
- Grant the IAM role both s3:GetObject on the source key and s3:PutObject on the destination prefix
- Unwrap errors.As(smithy.APIError) to see the exact S3 error code (AccessDenied/NoSuchKey/InvalidRequest)
- For objects >5GB, implement multipart upload copy (UploadPartCopy) instead of single CopyObject
- For SSE-KMS buckets, verify kms:Decrypt and kms:Encrypt grants on the key
Example fix
// before
newPath, err := svc.CopyFile(ctx, src, tenantID, kid)
if err != nil { return err }
// after
newPath, err := svc.CopyFile(ctx, src, tenantID, kid)
if err != nil {
var apiErr smithy.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "InvalidRequest" {
// fall back to multipart copy for large objects
}
return err
} Defensive patterns
Strategy: try-catch
Try / catch
newPath, err := svc.CopyFile(ctx, srcPath, tenantID, kid)
if err != nil {
var apiErr smithy.APIError
if errors.As(err, &apiErr) && apiErr.ErrorCode() == "AccessDenied" {
return fmt.Errorf("missing s3 permissions for copy: %w", err)
}
return err
} Prevention
- IAM needs s3:GetObject on source prefix and s3:PutObject on destination prefix
- For SSE-KMS buckets grant kms:Decrypt/kms:Encrypt
- Avoid CopyFile for objects >5GB; use multipart copy
- Ensure source file still exists before copy
When it happens
Trigger: CopyObject failing due to AccessDenied on source or destination, NoSuchKey on srcKey, KMS encryption key permission issues, or CopySource size exceeding the 5GB single-request limit.
Common situations: KMS-encrypted objects copied by a principal lacking kms:Decrypt/kms:Encrypt; cross-account or cross-region source without proper permissions; very large uploaded files breaking the 5GB CopyObject limit.
Related errors
- failed to delete file: %w
- failed to delete file from OBS: %w
- failed to copy file in OBS: %w
- failed to copy file in OSS: %w
- failed to load AWS config: %w
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/8ddea5956137e9b0.
Report an issue: GitHub.