{"record":{"id":"cf82d71b681068d6","repo":"Tencent/WeKnora","slug":"s3-copy-rejected-source-q-w","errorCode":null,"errorMessage":"s3 copy rejected source %q: %w","messagePattern":"s3 copy rejected source %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/file/s3.go","lineNumber":300,"sourceCode":"\t\tBucket: aws.String(s.bucketName),\n\t\tKey:    aws.String(objectName),\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete file: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// CopyFile copies an existing S3 object to a new knowledge-owned object using a\n// server-side CopyObject (no data leaves S3). The destination uses the same\n// layout as SaveFile. Returns ErrCrossBackendCopy when srcPath is not an s3:// path.\nfunc (s *s3FileService) CopyFile(ctx context.Context,\n\tsrcPath string, tenantID uint64, knowledgeID string,\n) (string, error) {\n\tsrcKey, err := s.parseS3FilePath(srcPath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"s3 copy rejected source %q: %w\", srcPath, ErrCrossBackendCopy)\n\t}\n\n\text := filepath.Ext(srcPath)\n\tdestKey := fmt.Sprintf(\"%s%d/%s/%s%s\", s.pathPrefix, tenantID, knowledgeID, uuid.New().String(), ext)\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// srcKey is already validated by parseS3FilePath -> SafeObjectKey.\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 S3: %w\", err)\n\t}\n\n\tnewPath := fmt.Sprintf(\"s3://%s/%s\", s.bucketName, destKey)","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/s3.go#L282-L318","documentation":"CopyFile rejects a source path that is not a valid s3:// URL by wrapping ErrCrossBackendCopy with 's3 copy rejected source %q'. parseS3FilePath failed, meaning the source belongs to a different storage backend (local disk, TOS) or is malformed. Copying across backends is intentionally unsupported.","triggerScenarios":"Calling CopyFile with srcPath not starting with 's3://' (e.g. a local file path or a TOS path), or a malformed s3:// URL that parseS3FilePath cannot extract a key from.","commonSituations":"Mixing storage backends after migrating config (files stored locally but backend switched to S3); passing a raw key instead of a full s3:// URL; knowledge entries created under a previous local-storage configuration.","solutions":["Ensure srcPath is the full path returned by SaveFile/SaveBytes (starts with s3://bucket/key)","Check the storage config has not changed since the source file was created; migrate the file first","Treat ErrCrossBackendCopy explicitly and copy via download+re-upload to the target backend","Log the offending srcPath to identify where the non-S3 path came from"],"exampleFix":"// before\nnewPath, err := svc.CopyFile(ctx, oldPath, tenantID, knowledgeID) // oldPath is local\n// after\nif !strings.HasPrefix(oldPath, \"s3://\") {\n    data, err := localSvc.GetFile(ctx, oldPath)\n    // then SaveBytes via the S3 service instead of CopyFile\n}\nnewPath, err := svc.CopyFile(ctx, oldPath, tenantID, knowledgeID)","handlingStrategy":"validation","validationCode":"if !strings.HasPrefix(srcPath, \"s3://\") {\n    return fmt.Errorf(\"%w: %s\", file.ErrCrossBackendCopy, srcPath)\n}","typeGuard":"func isS3Path(p string) bool { return strings.HasPrefix(p, \"s3://\") }","tryCatchPattern":"newPath, err := svc.CopyFile(ctx, srcPath, tenantID, kid)\nif errors.Is(err, file.ErrCrossBackendCopy) {\n    // fall back to download + re-upload via target backend\n}","preventionTips":["Store and pass full s3:// URLs, never bare keys","Check errors.Is(err, ErrCrossBackendCopy) and implement a cross-backend fallback path","Detect backend switches in config migration and migrate files proactively","Validate srcPath backend before constructing copy operations"],"tags":["aws","s3","validation","cross-backend"],"backgroundTag":"cross-backend-copy","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}