Skip to content

S3 storage: 403 SignatureDoesNotMatch when media filename contains non-ASCII characters (consecutive spaces left in metadata) #2279

Description

@cnlidongyun

Environment

  • WAHA: 2026.8.2 (CORE tier, GOWS engine)
  • Storage: WAHA_MEDIA_STORAGE=S3 with an S3-compatible provider (Tencent Cloud COS)
  • @aws-sdk/client-s3: 3.828.0 (resolved from ^3.633.0)

Problem

When an incoming media message has a filename containing non-ASCII characters (e.g. Chinese/CJK — very common for users in China), the S3 upload fails with 403 SignatureDoesNotMatch. WAHA then pushes the webhook with media.url = null and media.error = SignatureDoesNotMatch, so the media is lost for consumers.

Filenames that contain consecutive spaces (e.g. "Silk road Self driving.mp4") fail the same way, even without any non-ASCII characters.

Root cause

In MediaS3Storage.stringifyMetadata() non-ASCII characters are stripped from metadata values:

// only ascii allowed in value for S3
metadata[key] = value.replace(/[^\x20-\x7E]/g, '');

Stripping CJK characters leaves consecutive spaces inside the value:

"BODDI611045CE9TR 72 包天宇 20260922.pdf" → "BODDI611045CE9TR 72 20260922.pdf" (two spaces).

Per the AWS4 signing spec, CanonicalHeaders values must be trimmed and have consecutive spaces collapsed. aws-sdk-js-v3 (3.828.0) signs the header value as-is (no whitespace collapsing), while S3-compatible services that follow the spec (verified on Tencent Cloud COS) collapse the spaces when re-computing the signature server-side → the signatures never match → 403 SignatureDoesNotMatch.

Verification

Minimal experiment using the same SDK version and credentials:

Metadata value Result
'A B' (two consecutive spaces) 403 SignatureDoesNotMatch
'A B' (single space) 200 OK

Reproduces 100% of the time; every media message with a CJK filename fails.

Suggested fix

Collapse consecutive spaces (and trim) after stripping non-ASCII, so the value we sign matches what the service re-computes:

metadata[key] = value
  .replace(/[^\x20-\x7E]/g, '')
  .replace(/ {2,}/g, ' ')
  .trim();

This keeps metadata ASCII-safe and makes the client-side signature consistent with the server-side recomputation. After applying this patch locally, all previously failing uploads succeed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions