Editor colaborativo perdía cambio por error de surrogates en emoji

Fuentes: My Favorite Bugs: Invalid Surrogate Pairs
Editor colaborativo perdía cambio por error de surrogates en emoji
Imagen generada con IA

Este artículo narra la historia de un bug particularmente insidioso que el autor descubrió mientras trabajaba en un editor colaborativo basado en TipTap y Yjs. El problema se manifestó de forma silenciosa: el editor dejaba de guardar cambios sin dar errores visibles, haciendo que el contenido del usuario desapareciera misteriosamente al reabrir el documento. La causa raíz estaba relacionada con los pares de surrogates de Unicode utilizados para representar emoji que superan U+FFFF. JavaScript internamente almacena strings como UTF-16, donde emoji como 🤠 (U+1F920) requieren dos code units llamados surrogate pair (alto y bajo). Cuando una operación CRDT splitía esta pareja (por ejemplo, insertando un carácter exactamente entre dos emoji adyacentes), el método interno .slice() generaba un surrogate huérfano. Este fragmento inválido, al pasar por encodeURIComponent durante la sincronización, lanzaba un URIError no capturado, causando que el sync se detuviera sin notificación. El autor explica la distinción entre code units (lo que JavaScript cuenta con .length), code points (definición real de Unicode) y grapheme clusters (lo que un humano percibe como un carácter). Por ejemplo, 👨👨👧👧 son 11 code units, 7 code points, pero solo 1 grapeme cluster. La solución temporal incluyó agregar soporte offline y capturar globalmente el URIError para mostrar un modal al usuario. Este bug afectó a múltiples editores basados en Yjs, y el autor recomienda tener cuidado con operaciones que split strings en JavaScript cuando contienen emoji.