Tencent/WeKnora · warning
invalid file name: %w
Error message
invalid file name: %w
What it means
SaveBytes validates the incoming fileName with utils.SafeFileName before uploading; this error wraps a validation failure. The provided name was empty, contained illegal path characters, or could not be sanitized into a safe S3 key component, so nothing was uploaded.
Source
Thrown at internal/application/service/file/s3.go:328
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)
logger.Infof(ctx, "Copied S3 object %s to %s", srcPath, newPath)
return newPath, nil
}
// SaveBytes saves bytes data to S3 and returns the file path
// temp parameter is ignored for S3 (no auto-expiration support in this implementation)
func (s *s3FileService) SaveBytes(ctx context.Context, data []byte, tenantID uint64, fileName string, temp bool) (string, error) {
safeName, err := utils.SafeFileName(fileName)
if err != nil {
return "", fmt.Errorf("invalid file name: %w", err)
}
ext := filepath.Ext(safeName)
objectName := fmt.Sprintf("%s%d/exports/%s%s", s.pathPrefix, tenantID, uuid.New().String(), ext)
// Upload bytes to S3
reader := bytes.NewReader(data)
_, err = s.client.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(s.bucketName),
Key: aws.String(objectName),
Body: reader,
ContentLength: aws.Int64(int64(len(data))),
ContentType: aws.String(utils.GetContentTypeByExt(ext)),
})
if err != nil {
return "", fmt.Errorf("failed to upload bytes to S3: %w", err)
}
return fmt.Sprintf("s3://%s/%s", s.bucketName, objectName), nilView on GitHub (pinned to 988cbb0330)
Solutions
- Validate/sanitize fileName at the API boundary before calling SaveBytes (strip path components, reject empty)
- Provide a server-generated default name (e.g. uuid or timestamp) when the client filename is empty
- Unwrap the SafeFileName error to see which character/rule failed and normalize the input accordingly
- Reject unsafe names with a 400 response instead of a 500 from the storage layer
Example fix
// before
path, err := svc.SaveBytes(ctx, data, tenantID, r.Header.Get("X-Filename"), false)
// after
name := filepath.Base(r.Header.Get("X-Filename"))
if name == "" || name == "." {
name = uuid.New().String()
}
path, err := svc.SaveBytes(ctx, data, tenantID, name, false) Defensive patterns
Strategy: validation
Validate before calling
if fileName == "" { return errors.New("file name required") }
safe, err := utils.SafeFileName(fileName)
if err != nil { return fmt.Errorf("rejecting upload: %w", err) } Type guard
func isSafeFileName(name string) bool {
return name != "" && !strings.ContainsAny(name, "/\\\x00") && utils.SafeFileName(name) == nil == false || utils.SafeFileName(name) == nil
} Try / catch
path, err := svc.SaveBytes(ctx, data, tenantID, fileName, false)
if err != nil && strings.Contains(err.Error(), "invalid file name") {
return httputil.BadRequest("invalid file name")
} Prevention
- Sanitize user-supplied filenames at the HTTP boundary before storage
- Default to a generated name when the client sends none
- Strip directory components with filepath.Base
- Return 400 (client error) not 500 for invalid names
When it happens
Trigger: Calling SaveBytes with an empty fileName, a name containing path separators ('/', '../'), null bytes, or other characters rejected by SafeFileName.
Common situations: User-supplied upload filenames passed straight through from an HTTP multipart form; filenames from other filesystems (Windows backslashes); empty name when the client did not send a filename.
Related errors
- invalid file name: %w
- invalid file name: %w
- S3 access key and secret key must be provided together
- s3 copy rejected source %q: %w
- query is empty
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/e20100d9e6957116.
Report an issue: GitHub.