JavaScript encoding troubleshooting · Updated October 30, 2026

encodeURIComponent URIError: Find the Lone Surrogate

A lone UTF-16 surrogate makes encodeURIComponent throw URIError. Paste text or a JSON string literal below to locate the failing code unit. Reject damaged input when exact preservation matters; replacement changes the input.

Runs in this browser. JSON mode lets you reproduce code units that a normal paste may not preserve.

Find the first failing boundary

  1. Keep the value before encoding. For a JavaScript failure, capture it with JSON.stringify so the escape survives copying.
  2. Choose JSON mode and paste the complete quoted string. Inspect the zero-based UTF-16 indexes, not a count of visible characters.
  3. Compare direct encoding with the replacement preview. Do not silently substitute the preview for an identifier or signed value.
  4. Fix the producer if truncation split a surrogate pair. If your application deliberately permits replacement, record that policy and test the receiving system.

Nine inputs: rejection and replacement are different results

We ran these synthetic fixtures on October 30, 2026 using Node v24.19.0. The downloaded regression checks the direct result, the exact invalid indexes, and the repaired round trip. It also asserts that a lone high surrogate and a genuine replacement character collide after replacement. These cases measure behavior, not a frequency of real-world failures.

FixtureUTF-16 unitsDirect encodeURIComponentAfter toWellFormedURLSearchParams
plain0041AAq=A
paird83d de00%F0%9F%98%80%F0%9F%98%80q=%F0%9F%98%80
highd800URIError%EF%BF%BDq=%EF%BF%BD
lowdc00URIError%EF%BF%BDq=%EF%BF%BD
reverseddc00 d800URIError%EF%BF%BD%EF%BF%BDq=%EF%BF%BD%EF%BF%BD
slice-halfd83dURIError%EF%BF%BDq=%EF%BF%BD
replacementfffd%EF%BF%BD%EF%BF%BDq=%EF%BF%BD
inside0041 d800 0042URIErrorA%EF%BF%BDBq=A%EF%BF%BDB
two-highd800 d800URIError%EF%BF%BD%EF%BF%BDq=%EF%BF%BD%EF%BF%BD

The collision a successful request can hide

The high fixture starts with D800. The replacement fixture starts with FFFD. They are distinct JavaScript strings, yet the replacement pipeline produces %EF%BF%BD for both. URLSearchParams also produces q=%EF%BF%BD for both. A request that no longer throws can therefore have lost the distinction you were trying to transmit.

The slice-half fixture starts as a valid emoji pair, then slice(0,1) keeps only its first code unit. Encoding the original works; encoding that prefix throws. A limit measured in JavaScript string length can split the pair even when the original input looked valid. Iterate code points if your intended limit is code points; that still is not a count of grapheme clusters.

The reversed fixture contains two invalid positions. Removing only the first does not guarantee a valid remainder. The inside fixture locates the failure at index 1, while ordinary A and B survive a deliberate replacement. These distinctions are why the checker reports every bad code unit rather than only displaying a generic failure.

Run the same fixture set

Save the regression script under downloads/ and the shared checker core under assets/ in the same parent folder. Run the command below on a Node runtime supporting toWellFormed. The recorded JSON output contains every row and the runtime version. An assertion failure exits nonzero; success prints nine rows.

node downloads/uri-surrogate-regression.cjs

Does encoding a URI component repair my string?

No. Direct encoding rejects these malformed strings. The repair preview is an explicit alternative that replaces lone surrogates; it cannot recover the original intended character.

Is this the same as a malformed percent escape?

No. This checker examines the JavaScript string before encoding. For an already encoded URL, use the URL decoder. For ordinary component syntax and whole-URL differences, see the JavaScript URL encoding guide or the URL encoding tool.

API behavior checked October 30, 2026: MDN encodeURIComponent exceptions and lone-surrogate example. The fixture selection, index diagnostics, paired truncation case and collision assertions are our reproducible checks, not a claim that these API facts are exclusive.