Tencent/WeKnora · error
s3 copy rejected source %q: %w
Error message
s3 copy rejected source %q: %w
What it means
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.
Source
Thrown at internal/application/service/file/s3.go:300
Bucket: aws.String(s.bucketName),
Key: aws.String(objectName),
})
if err != nil {
return fmt.Errorf("failed to delete file: %w", err)
}
return nil
}
// CopyFile copies an existing S3 object to a new knowledge-owned object using a
// server-side CopyObject (no data leaves S3). The destination uses the same
// layout as SaveFile. Returns ErrCrossBackendCopy when srcPath is not an s3:// path.
func (s *s3FileService) CopyFile(ctx context.Context,
srcPath string, tenantID uint64, knowledgeID string,
) (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)View on GitHub (pinned to 988cbb0330)
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
Example fix
// before
newPath, err := svc.CopyFile(ctx, oldPath, tenantID, knowledgeID) // oldPath is local
// after
if !strings.HasPrefix(oldPath, "s3://") {
data, err := localSvc.GetFile(ctx, oldPath)
// then SaveBytes via the S3 service instead of CopyFile
}
newPath, err := svc.CopyFile(ctx, oldPath, tenantID, knowledgeID) Defensive patterns
Strategy: validation
Validate before calling
if !strings.HasPrefix(srcPath, "s3://") {
return fmt.Errorf("%w: %s", file.ErrCrossBackendCopy, srcPath)
} Type guard
func isS3Path(p string) bool { return strings.HasPrefix(p, "s3://") } Try / catch
newPath, err := svc.CopyFile(ctx, srcPath, tenantID, kid)
if errors.Is(err, file.ErrCrossBackendCopy) {
// fall back to download + re-upload via target backend
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- obs copy rejected source %q: %w
- S3 access key and secret key must be provided together
- failed to load AWS config: %w
- failed to get file from S3: %w
- failed to delete file: %w
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/cf82d71b681068d6.
Report an issue: GitHub.