Why encodeURIComponent throws URIError on a cut emoji: lone surrogates
Cut an emoji in half: encodeURIComponent throws URIError, TextEncoder writes EF BF BD. Lone UTF-16 surrogates and isWellFormed()/toWellFormed(), per ECMA-262 and Unicode 17.0.
A username cut short for a URL can end like this:
const cut = "ab😀".slice(0, 3); // "ab" + "\uD83D"
encodeURIComponent(cut); // URIError
new TextEncoder().encode(cut); // 61 62 EF BF BD
One call throws, the other quietly changes the bytes. The cause is a lone surrogate.
Half an emoji
In UTF-16, 😀 (U+1F600) is two code units, D83D DE00 (Unicode, Table 3-5). slice counts code units, so only D83D survives.
Unicode sets D800–DFFF aside for surrogates and excludes them from scalar values (D71–D76). A lone surrogate code unit is ill-formed UTF-16 (D91). A JavaScript string is a sequence of 16-bit values, so it can hold one anyway.
No place in UTF-8
RFC 3629 §3 prohibits encoding U+D800–U+DFFF in UTF-8, so an API producing UTF-8 must stop or substitute.
encodeURI and encodeURIComponent stop: ECMA-262’s Encode (19.2.6.5) throws URIError on an unpaired surrogate. Unlike the decoding error in the URL encoding post, this one happens when encoding.
TextEncoder substitutes. encode() takes a USVString, and the Encoding Standard notes that this conversion turns lone surrogates into U+FFFD (�), which is EF BF BD in UTF-8. The emoji is lost without an error.
Check, then choose
ECMA-262’s isWellFormed() (22.1.3.10) detects lone surrogates; toWellFormed() (22.1.3.31) returns a copy with each one replaced by U+FFFD.
cut.isWellFormed(); // false
encodeURIComponent(cut.toWellFormed()); // "ab%EF%BF%BD"
� still loses data, so avoid cutting by code units; Why character counts disagree covers cutting by code points or visible characters. Check isWellFormed() where a string leaves as bytes, and pick the exception or the replacement on purpose.
Sources
- ECMAScript 2026, 19.2.6 URI Handling Functions: 19.2.6.3–19.2.6.5
- ECMAScript 2026, 22.1 String Objects: 22.1.3.10, 22.1.3.22, 22.1.3.31
- ECMAScript 2026, 7.2.7 IsStringWellFormedUnicode
- WHATWG Web IDL: 2.13.19 USVString
- WHATWG Encoding Standard: 7.4 TextEncoder, 7.6 TextEncoderStream
- WHATWG Infra Standard: 4.6 Code points, 4.7 Strings
- The Unicode Standard 17.0, chapter 3: 3.8 Surrogates, 3.9 Unicode Encoding Forms, Table 3-5
- RFC 3629, UTF-8, a transformation format of ISO 10646: section 3
Links and content were checked on 8 October 2026.