Tencent/WeKnora · error · ErrCrossBackendCopy
obs copy rejected source %q: %w
Error message
obs copy rejected source %q: %w
What it means
CopyFile explicitly rejects source paths that do not start with this service's own prefix (proxy domain or obs:// scheme). Because parseObsFilePath silently returns unknown-format paths unchanged, this guard detects cross-backend copies (e.g. copying an OSS or local-disk path into OBS) and aborts with ErrCrossBackendCopy.
Source
Thrown at internal/application/service/file/obs.go:253
if s.proxyDomain != "" {
return s.proxyDomain + "/" + strings.TrimPrefix(objectKey, "/"), nil
}
return fmt.Sprintf("%s/%s/%s", s.endpoint, s.bucketName, strings.TrimPrefix(objectKey, "/")), nil
}
// CopyFile copies an existing OBS object to a new knowledge-owned object using a
// server-side CopyObject (OBS is S3-compatible). The destination uses the same
// layout as SaveFile. Returns ErrCrossBackendCopy when srcPath does not belong
// to this OBS service.
func (s *obsFileService) CopyFile(ctx context.Context,
srcPath string, tenantID uint64, knowledgeID string,
) (string, error) {
// Reject paths that do not use this service's prefix (proxy domain or obs://).
// parseObsFilePath falls back to returning the raw input for unknown prefixes,
// so guard explicitly here to detect cross-backend sources.
if !strings.HasPrefix(srcPath, s.getPrifix()) {
return "", fmt.Errorf("obs copy rejected source %q: %w", srcPath, ErrCrossBackendCopy)
}
srcKey, err := s.parseObsFilePath(srcPath)
if err != nil {
return "", fmt.Errorf("obs copy rejected source %q: %w", srcPath, ErrCrossBackendCopy)
}
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),View on GitHub (pinned to 988cbb0330)
Solutions
- Ensure srcPath was produced by this same OBS service (starts with proxyDomain prefix or obs:// prefix)
- For genuine cross-backend copies, download via the source service and upload via SaveFile/SaveBytes instead of CopyFile
- Check tenant configuration so each request uses the file service that owns the stored path
- Validate the path prefix before calling and surface a clear 'unsupported source backend' message to the user
Example fix
// before
newPath, err := obsSvc.CopyFile(ctx, ossPath, tenantID, kid) // rejected
// after
if isOSSPath(ossPath) {
r, _ := ossSvc.GetFile(ctx, ossPath)
defer r.Close()
newPath, err = obsSvc.SaveBytes(ctx, tenantID, kid, readAll(r), ext)
} else {
newPath, err = obsSvc.CopyFile(ctx, ossPath, tenantID, kid)
} Defensive patterns
Strategy: validation
Validate before calling
func copySourceOK(srcPath, prefix string) bool {
return strings.HasPrefix(srcPath, prefix)
}
// call before CopyFile:
if !copySourceOK(srcPath, obsPrefix) {
return ErrCrossBackendCopy
} Type guard
func isOBSBackendPath(p string) bool {
return strings.HasPrefix(p, "obs://") || strings.HasPrefix(p, proxyDomain)
} Try / catch
newPath, err := svc.CopyFile(ctx, src, tenantID, kid)
if errors.Is(err, file.ErrCrossBackendCopy) {
http.Error(w, "source file belongs to a different storage backend", http.StatusBadRequest)
return
} Prevention
- Keep a backend marker with each stored path so the right service is selected
- Route all copy operations through a dispatcher that picks the owning file service
- Never pass raw user URLs directly to CopyFile
When it happens
Trigger: Calling CopyFile with srcPath that does not begin with s.getPrifix() — e.g. a path produced by the OSS service, a local file path like /data/uploads/x.png, or a URL pointing at another bucket's proxy domain.
Common situations: Migrating between storage backends and passing old OSS paths to the OBS service; multi-tenant configs where different tenants use different storage services; copy-pasting a file URL from a different environment.
Related errors
- failed to copy file in OBS: %w
- oss copy rejected source %q: %w
- invalid source path: %w
- s3 copy rejected source %q: %w
- invite code has expired
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/40da12871a29f493.
Report an issue: GitHub.