Intl.Locale: throw RangeError for structurally valid tags that exceed ICU capacity - #35900
Intl.Locale: throw RangeError for structurally valid tags that exceed ICU capacity#35900robobun wants to merge 2 commits into
Conversation
… ICU capacity
new Intl.Locale(tag) threw TypeError ("failed to initialize Locale") for a
structurally valid BCP-47 tag once it overflowed ICU's ULOC_FULLNAME_CAPACITY
buffer (23+ variant subtags, ~186 chars). Every other Intl entry point
(DateTimeFormat, NumberFormat, Collator, getCanonicalLocales, localeCompare)
throws RangeError for the same input via canonicalizeLocaleList, and so does
V8/Node.
The source change lives in JavaScriptCore (IntlLocale.cpp); this bumps
WEBKIT_VERSION to the oven-sh/WebKit#350 preview tarball and adds a test
asserting every Intl path agrees on RangeError for an oversized-but-valid tag.
|
Companion WebKit change: oven-sh/WebKit#350 (preview tarball CI state for b71ad1a (#82299): Binary-size check: the ~+530 KB delta is against canary baseline #79916 (commit ae4b17d, 12 main commits behind this PR's base). Those 12 commits include Ready for review. Once oven-sh/WebKit#350 merges, |
|
Warning Review limit reached
Next review available in: 14 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
Comment |
|
Updated 7:09 AM PT - Jul 26th, 2026
❌ @robobun, your commit b71ad1a has 1 failures in
Add 🧪 To try this PR locally: bunx bun-pr 35900That installs a local version of the PR into your bun-35900 --bun |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Beyond the inline nit, I checked that the non-sha WEBKIT_VERSION format doesn't break the build scripts — prebuiltUrl() and prebuiltDestDir() both have explicit startsWith("autobuild-") branches that handle preview tags, so tarball URL and cache-dir keying resolve correctly. The pin to an unmerged preview tarball is a coordination call for a maintainer.
Extended reasoning...
The only inline finding is a nit about the 22/23-variant boundary sitting within a few bytes of an undocumented ICU threshold that may differ on macOS's system libicucore. Separately I verified the autobuild-preview-pr-350-* version string is handled by the existing startsWith("autobuild-") cases in prebuiltUrl (skips double-prefixing the release tag) and prebuiltDestDir (uses the full tag rather than a 16-char truncation), so the version bump itself won't break tarball resolution. Whether to land main pointed at a preview WebKit PR before oven-sh/WebKit#350 merges is a process decision I'm leaving to a human.
…23 variants Windows ICU 73.2 grows its canonicalization buffer and accepts arbitrarily long structurally-valid tags, so the 23-variant tag constructs there and the hard-coded RangeError assertion showed 'no throw' for every entry. The capacity is an ICU-internal detail (Linux/macOS fail at 23, Windows never does); probe for it via getCanonicalLocales and skip the RangeError assertion on builds without a limit. A Linux-only guard ensures the probe never silently degrades to a skip on the platform where the bug reproduces.
There was a problem hiding this comment.
The revised test in b71ad1a addresses the platform-fragility concern well — probing for the threshold and guarding against silent skip on Linux is the right shape. I'm deferring because WEBKIT_VERSION is being pinned to a preview tarball of an unmerged companion PR (oven-sh/WebKit#350); whether to land main against a preview build vs. wait for #350 to merge is a maintainer call, and the actual C++ fix isn't reviewable from this repo.
What was reviewed:
- Probe loop finds the first N in [20..200] where
getCanonicalLocalesthrows, then asserts all seven Intl paths agree on RangeError for that tag — no hardcoded boundary. - Linux-only
expect(overflowTag).toBeDefined()guard prevents the RangeError test from silently becoming a skip on the platform where the bug reproduces. - "Still constructs" test now uses 5 variants (42 bytes), well clear of any ICU threshold.
Extended reasoning...
Overview
Two files: scripts/build/deps/webkit.ts bumps WEBKIT_VERSION from a main-branch sha to autobuild-preview-pr-350-db4518f8 (a preview tarball of oven-sh/WebKit#350, branched from the previous pin). test/js/web/intl/intl.test.ts adds an Intl.Locale describe block with three tests covering the RangeError-vs-TypeError fix. The actual behavioral change lives in JavaScriptCore's IntlLocale::initializeLocale in the companion WebKit PR and is not visible in this diff.
Security risks
None. The change swaps one thrown error class for another on an ICU buffer-overflow path that was already an error; no new inputs are accepted, no validation is loosened. The WebKit tarball is fetched from the same oven-sh/WebKit releases mechanism as before.
Level of scrutiny
WebKit version bumps warrant maintainer review by default — they replace the entire JS engine binary. Here the author states (and the binary-size comment corroborates) that the delta is a 320-byte change to libJavaScriptCore.a with all other libs byte-identical, which is consistent with a two-line throwTypeError→throwRangeError swap. But the pin is to an unmerged preview tarball with an explicit "revert to the merged-main sha once #350 lands" TODO in the comment. Whether Bun main should temporarily depend on a preview WebKit build, or whether this PR should wait for oven-sh/WebKit#350 to merge first, is a release-process decision I shouldn't make.
Other factors
My earlier inline concern about the hardcoded 22/23-variant boundary turned out to be real (Windows ICU 73.2 has no fixed limit and the original test failed there). The author's fix in b71ad1a is solid: the probe discovers the platform's actual threshold, the RangeError assertion skips where no limit exists, and a separate Linux-only test asserts the probe does find a limit so the main assertion can't quietly go vacuous on bundled ICU. The "still constructs" case moved to 5 variants. The probe loop runs at describe-body time (module load), which is fine — it's ~180 cheap sync calls. No remaining test-quality concerns.
Problem
new Intl.Locale(tag)throws TypeError (failed to initialize Locale) for a structurally valid BCP-47 tag once it exceeds ICU's internalULOC_FULLNAME_CAPACITYbuffer (~180 chars, e.g. 23 distinct variant subtags). Every other Intl entry point throws RangeError for the same input, and so does Node:So
catch (e) { if (e instanceof RangeError) /* invalid locale */ }misses only theIntl.Localeconstructor.Cause
IntlLocale::initializeLocalevalidates the tag withisStructurallyValidLanguageTag(throwingRangeErroron failure) before reachingLocaleIDBuilder::canonicalize()/toCanonical(). At that point the tag is known to be well-formed; the only failure mode is ICU's fixed-size buffer overflow. Those two failure sites threwthrowTypeError, while the equivalent path incanonicalizeLocaleList(used by every other Intl constructor) throwscreateRangeError.Fix
The source change is in JavaScriptCore: oven-sh/WebKit#350 switches both sites to
throwRangeError.This PR:
WEBKIT_VERSIONto the Node.js for loops are faster #350 preview tarball (autobuild-preview-pr-350-db4518f8, branched from the previousWEBKIT_VERSION549170099226f816so the only delta is the IntlLocale change).test/js/web/intl/intl.test.tsasserting all seven Intl paths agree onRangeErrorfor the oversized tag, and that 22 variants (under capacity) still constructs.Once oven-sh/WebKit#350 is merged,
WEBKIT_VERSIONshould be updated to the merged main sha.Verification
Note: the source change lives in
scripts/build/deps/webkit.ts(notsrc/), so the fail-before gate'sgit stash -- src/cannot revert it; both gate runs will link the fixed WebKit and pass.no test proof · iteration 1 · Platform-specific test-only change; deferring to CI.