Skip to content

fix: normalize the defaultCharset option to lower case - #764

Open
spokodev wants to merge 2 commits into
expressjs:masterfrom
spokodev:fix/normalize-default-charset-case
Open

fix: normalize the defaultCharset option to lower case#764
spokodev wants to merge 2 commits into
expressjs:masterfrom
spokodev:fix/normalize-default-charset-case

Conversation

@spokodev

@spokodev spokodev commented Sep 2, 2026

Copy link
Copy Markdown

Charset names are case-insensitive (RFC 2978). body-parser already lower-cases the charset parsed from the request's Content-Type (getCharset()), but the developer-configured defaultCharset option is stored verbatim, so a differently-cased but valid value breaks:

  • json({ defaultCharset: 'UTF-8' }) rejects a normal application/json request (no charset param) with 415 unsupported charset "UTF-8", because isValidCharset checks charset.slice(0, 4) === 'utf-'.
  • urlencoded({ defaultCharset: 'UTF-8' }) throws at construction: option defaultCharset must be either utf-8 or iso-8859-1.

Lower-casing defaultCharset in normalizeOptions fixes both call sites, matching how the request-header charset is already normalized.

@kilisamemarisaaa

Copy link
Copy Markdown
Contributor

I reviewed the exact head cd72cdf59e833904a78b4c1299f2c43dae29e56a against the current base.

The normalization fixes mixed-case charset names before downstream charset validation while preserving an explicit request charset override. I verified the JSON and urlencoded paths with mixed-case defaults (including UTF-8, UTF-16LE, and ISO-8859-1) and ran the full suite: 274 passing, 4 pending. npm run lint also passes.

The change is focused and the added unit test covers the regression. GitHub does not allow my account to submit a formal approval on this repository, so I am recording the verification evidence here for the maintainers.

@kilisamemarisaaa kilisamemarisaaa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified this change against exact head cd72cdf with real HTTP requests.

Base/head comparison:

  • On the base, json({ defaultCharset: 'UTF-8' }) returns 415 for JSON without an explicit charset; the head parses the same UTF-8 payload successfully.
  • Mixed-case UtF-16Le correctly decodes a UTF-16LE JSON body on the head.
  • urlencoded({ defaultCharset: 'IsO-8859-1' }) correctly parses name=%E9 as é on the head; the base rejects the uppercase spelling at construction.
  • An explicit Content-Type charset=UTF-8 still overrides an ISO-8859-1 default and parses successfully.

I also ran the complete test suite with the exact head implementation: 273 passing, 4 pending, 0 failures.

I checked non-string and falsy option boundaries as well. Truthy non-string values now fail earlier at construction, but those values are outside the documented string contract; null, undefined, empty string, false, and zero retain the existing UTF-8 fallback behavior. I did not find a functional regression in the supported inputs.

@kilisamemarisaaa

Copy link
Copy Markdown
Contributor

Correction to my review above: I reran
pm test at exact head cd72cdf; the complete suite is 274 passing, 4 pending, 0 failures (not 273). The charset behavior findings and no-regression conclusion are unchanged.

Comment thread lib/utils.js Outdated
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@spokodev

spokodev commented Sep 7, 2026

Copy link
Copy Markdown
Author

Applied, thanks. 274 passing, lint clean.

One behaviour note so the choice is explicit: the two forms differ only on non-string input. undefined, null and `` still fall back to utf-8 either way, but `defaultCharset: 0` or `false` used to fall back and now throws `TypeError`, since `?.` only short-circuits on `null`/`undefined`. Both forms already threw on truthy non-strings, and the option is documented as a string, so this looks like the right side of the trade to me — flagging it only in case you would rather it stayed lenient.

@UlisesGascon UlisesGascon self-assigned this Sep 8, 2026
@Phillip9587

Copy link
Copy Markdown
Member

I think we decided to introduce stricter option validation in the next major release, but I'm not entirely sure anymore. Right now we're in an awkward middle ground. We're neither fully strict nor fully lenient. I'm not sure how we should move forward here. Any opinions, @UlisesGascon @bjohansebas?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants