{"record":{"id":"83cc55323f5bba27","repo":"Tencent/WeKnora","slug":"failed-to-copy-file-in-obs-w","errorCode":null,"errorMessage":"failed to copy file in OBS: %w","messagePattern":"failed to copy file in OBS: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/file/obs.go","lineNumber":276,"sourceCode":"\t}\n\n\text := filepath.Ext(srcPath)\n\tvar destKey string\n\tif s.pathPrefix != \"\" {\n\t\tdestKey = fmt.Sprintf(\"%s/%d/%s/%s%s\", s.pathPrefix, tenantID, knowledgeID, uuid.New().String(), ext)\n\t} else {\n\t\tdestKey = fmt.Sprintf(\"%d/%s/%s%s\", tenantID, knowledgeID, uuid.New().String(), ext)\n\t}\n\n\t// CopySource is \"bucket/key\"; the '/' separators must NOT be percent-encoded\n\t// (url.PathEscape would turn them into %2F and break the bucket/key split).\n\t_, err = s.client.CopyObject(ctx, &s3.CopyObjectInput{\n\t\tBucket:     aws.String(s.bucketName),\n\t\tCopySource: aws.String(s.bucketName + \"/\" + srcKey),\n\t\tKey:        aws.String(destKey),\n\t})\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to copy file in OBS: %w\", err)\n\t}\n\n\tprefix := s.getPrifix()\n\tvar newPath string\n\tif s.proxyDomain != \"\" {\n\t\tnewPath = fmt.Sprintf(\"%s%s\", prefix, destKey)\n\t} else {\n\t\tnewPath = fmt.Sprintf(\"%s%s/%s\", prefix, s.bucketName, destKey)\n\t}\n\tlogger.Infof(ctx, \"Copied OBS object %s to %s\", srcPath, newPath)\n\treturn newPath, nil\n}\n\nfunc (s *obsFileService) SaveBytes(ctx context.Context, data []byte, tenantID uint64, fileName string, temp bool) (string, error) {\n\text := filepath.Ext(fileName)\n\n\tvar objectKey string\n\tif temp {","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/obs.go#L258-L294","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nif err != nil { return \"\", fmt.Errorf(\"failed to copy file in OBS: %w\", err) }\n// after\nif err != nil {\n    var nf *types.NotFound\n    if errors.As(err, &nf) {\n        return \"\", fmt.Errorf(\"source object disappeared before copy: %w\", err)\n    }\n    if isRetryableNetErr(err) {\n        return s.CopyFile(ctx, srcPath, tenantID, knowledgeID)\n    }\n    return \"\", fmt.Errorf(\"failed to copy file in OBS: %w\", err)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"newPath, err := svc.CopyFile(ctx, src, tenantID, kid)\nif err != nil {\n    if isRetryableNetErr(err) {\n        newPath, err = svc.CopyFile(ctx, src, tenantID, kid) // idempotent per destKey\n    }\n    if err != nil {\n        return fmt.Errorf(\"server-side copy failed: %w\", err)\n    }\n}","preventionTips":["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"],"tags":["obs","s3","copy","network"],"backgroundTag":"object-storage-copy-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}