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), nil

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Validate/sanitize fileName at the API boundary before calling SaveBytes (strip path components, reject empty)
  2. Provide a server-generated default name (e.g. uuid or timestamp) when the client filename is empty
  3. Unwrap the SafeFileName error to see which character/rule failed and normalize the input accordingly
  4. 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

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


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/e20100d9e6957116. Report an issue: GitHub.