Tencent/WeKnora · error
invalid file name: %w
Error message
invalid file name: %w
What it means
SaveBytes validates the caller-supplied fileName with secutils.SafeFileName before storing bytes under <baseDir>/<tenantID>/exports. When the name contains path separators, traversal sequences (../), illegal characters, or is empty, SafeFileName returns an error and SaveBytes wraps it as 'invalid file name'.
Source
Thrown at internal/application/service/file/local.go:224
return "", fmt.Errorf("failed to copy file content: %w", err)
}
relPath, _ := filepath.Rel(s.baseDir, dstPath)
newPath := localScheme + filepath.ToSlash(relPath)
logger.Infof(ctx, "Copied local file %s to %s", srcPath, newPath)
return newPath, nil
}
// SaveBytes saves bytes data to a file and returns the file path
// temp parameter is ignored for local storage (no auto-expiration support)
// fileName 仅允许安全文件名,禁止路径遍历(如 ../../)
func (s *localFileService) SaveBytes(ctx context.Context, data []byte, tenantID uint64, fileName string, temp bool) (string, error) {
logger.Infof(ctx, "Saving bytes data: fileName=%s, size=%d, tenantID=%d, temp=%v", fileName, len(data), tenantID, temp)
safeName, err := secutils.SafeFileName(fileName)
if err != nil {
logger.Errorf(ctx, "Invalid fileName for SaveBytes: %v", err)
return "", fmt.Errorf("invalid file name: %w", err)
}
// Create storage directory with tenant ID
dir := filepath.Join(s.baseDir, fmt.Sprintf("%d", tenantID), "exports")
if err := os.MkdirAll(dir, 0o755); err != nil {
logger.Errorf(ctx, "Failed to create directory: %v", err)
return "", fmt.Errorf("failed to create directory: %w", err)
}
// Generate unique filename using timestamp
ext := filepath.Ext(safeName)
baseName := safeName[:len(safeName)-len(ext)]
uniqueFileName := fmt.Sprintf("%s_%d%s", baseName, time.Now().UnixNano(), ext)
filePath := filepath.Join(dir, uniqueFileName)
// Write data to file
if err := os.WriteFile(filePath, data, 0o644); err != nil {
logger.Errorf(ctx, "Failed to write file: %v", err)View on GitHub (pinned to 988cbb0330)
Solutions
- Sanitize the filename before calling SaveBytes: strip directory components (filepath.Base) and replace disallowed characters with '_' or a generated UUID.
- Never build storage names from raw user input — pass a server-generated name (UUID/timestamp + safe extension) and keep the original only as metadata.
- Inspect the wrapped error to see which rule SafeFileName enforced and adjust the input accordingly.
- If a valid name is being rejected, compare it against the sanitizer's allowlist in secutils.SafeFileName and normalize (NFC, strip controls) beforehand.
Example fix
// before
newPath, err := svc.SaveBytes(ctx, data, tenantID, rawUserFilename, false)
// after
safe := filepath.Base(rawUserFilename)
safe = strings.Map(func(r rune) rune {
if r < 32 || strings.ContainsRune("/\\:*?\"<>|", r) {
return '_'
}
return r
}, safe)
if safe == "" || safe == "." || safe == ".." {
safe = fmt.Sprintf("upload-%d", time.Now().UnixNano())
}
newPath, err := svc.SaveBytes(ctx, data, tenantID, safe, false) Defensive patterns
Strategy: validation
Validate before calling
func validFileName(name string) bool {
if name == "" || len(name) > 255 {
return false
}
if name != filepath.Base(name) || name == "." || name == ".." {
return false
}
return !strings.ContainsAny(name, "/\\:*?\"<>|") && !strings.ContainsFunc(name, func(r rune) bool { return r < 32 || r == 127 })
}
// call validFileName(fileName) before SaveBytes Type guard
func isSafeFileName(s string) bool {
return validFileName(s) // reuses validation above
} Try / catch
newPath, err := svc.SaveBytes(ctx, data, tenantID, fileName, temp)
if err != nil {
if strings.HasPrefix(err.Error(), "invalid file name") {
// fall back to a server-generated name
fileName = fmt.Sprintf("upload-%d%s", time.Now().UnixNano(), filepath.Ext(fileName))
newPath, err = svc.SaveBytes(ctx, data, tenantID, fileName, temp)
}
if err != nil {
return fmt.Errorf("save failed: %w", err)
}
} Prevention
- Never store raw user-supplied filenames; generate server-side names (UUID/timestamp) and keep originals as metadata only.
- Apply filepath.Base and character whitelisting to any filename crossing a trust boundary.
- Handle multipart upload metadata: browsers may send full client paths or exotic Unicode names.
- Add unit tests for SaveBytes with traversal ('../'), empty, and illegal-character inputs.
When it happens
Trigger: Calling SaveBytes with fileName containing '/' or '\\', '..' path segments, control/illegal characters, an empty string, or a name that fails the sanitizer's allowlist (e.g. reserved characters on the target OS).
Common situations: Storing user-uploaded filenames verbatim (browser sends odd names like 'report/../x.pdf' or a filename with a full path from Windows clients); test harnesses passing empty names; i18n filenames with characters rejected by the sanitizer.
Related errors
- invalid file path: %w
- invalid source path: %w
- invalid file path: %w
- object key contains path traversal
- join request not found
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/0ca23271246cbd96.
Report an issue: GitHub.