Tencent/WeKnora · error
failed to copy file in OBS: %w
Error message
failed to copy file in OBS: %w
What it means
CopyFile wraps the error from s3 CopyObject when the server-side copy within the same bucket fails. At this point the source key passed validation; the failure is in the OBS API call itself (source missing, permissions, size limits, or network).
Source
Thrown at internal/application/service/file/obs.go:276
}
ext := filepath.Ext(srcPath)
var destKey string
if s.pathPrefix != "" {
destKey = fmt.Sprintf("%s/%d/%s/%s%s", s.pathPrefix, tenantID, knowledgeID, uuid.New().String(), ext)
} else {
destKey = fmt.Sprintf("%d/%s/%s%s", 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).
_, 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 OBS: %w", err)
}
prefix := s.getPrifix()
var newPath string
if s.proxyDomain != "" {
newPath = fmt.Sprintf("%s%s", prefix, destKey)
} else {
newPath = fmt.Sprintf("%s%s/%s", prefix, s.bucketName, destKey)
}
logger.Infof(ctx, "Copied OBS object %s to %s", srcPath, newPath)
return newPath, nil
}
func (s *obsFileService) SaveBytes(ctx context.Context, data []byte, tenantID uint64, fileName string, temp bool) (string, error) {
ext := filepath.Ext(fileName)
var objectKey string
if temp {View on GitHub (pinned to 988cbb0330)
Solutions
- Unwrap and check for NoSuchKey — verify the source object still exists (HeadObject) and handle the race
- Ensure credentials have both s3:GetObject (source) and s3:PutObject (destination) permissions
- For objects near/above 5GB, perform a multipart copy instead of a single CopyObject
- Retry transient network errors with backoff; the operation is idempotent for a fixed destKey
Example fix
// before
if err != nil { return "", fmt.Errorf("failed to copy file in OBS: %w", err) }
// after
if err != nil {
var nf *types.NotFound
if errors.As(err, &nf) {
return "", fmt.Errorf("source object disappeared before copy: %w", err)
}
if isRetryableNetErr(err) {
return s.CopyFile(ctx, srcPath, tenantID, knowledgeID)
}
return "", fmt.Errorf("failed to copy file in OBS: %w", err)
} Defensive patterns
Strategy: retry
Try / catch
newPath, err := svc.CopyFile(ctx, src, tenantID, kid)
if err != nil {
if isRetryableNetErr(err) {
newPath, err = svc.CopyFile(ctx, src, tenantID, kid) // idempotent per destKey
}
if err != nil {
return fmt.Errorf("server-side copy failed: %w", err)
}
} Prevention
- Grant both GetObject (source) and PutObject (destination) to the copy job's credentials
- Use multipart copy for objects near the 5GB CopyObject limit
- Handle the deleted-source race by checking HeadObject before copy and returning a clear error
When it happens
Trigger: CopyObject fails because srcKey no longer exists (404), credentials lack both read on source and write on destination, the object exceeds the 5GB server-side copy limit, or a network/endpoint error occurs.
Common situations: Source object deleted between validation and copy (TOCTOU); IAM policy allowing PutObject but not GetObject; copying very large files that exceed the S3 CopyObject size limit; wrong region causing signature mismatch.
Related errors
- failed to upload file to OBS: %w
- failed to get file from OBS: %w
- failed to delete file from OBS: %w
- obs copy rejected source %q: %w
- failed to upload bytes to OBS: %w
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/83cc55323f5bba27.
Report an issue: GitHub.