Errores y source maps: trazas legibles en producción
Wrangler minifica tu código antes de desplegarlo, así que las trazas de pila de producción apuntan a un engrudo ilegible. Subir source maps con upload_source_maps cierra la brecha: Cloudflare los guarda en privado y remapea las excepciones para que veas archivo, línea y función reales, sin exponer nunca tu fuente al público.
Un usuario dispara una excepción en tu Worker y la traza que llega a tus logs dice TypeError en index.js:1:48210. Esa línea es inútil: apunta al bundle minificado de una sola línea, con nombres de una letra, que Wrangler generó para producción —no a tu código—. Existe una brecha semántica entre lo que escribiste y lo que corre, y una traza cruda vive del lado equivocado de esa brecha. Los source maps son el puente: le enseñan a Cloudflare a traducir esa posición de vuelta a carrito.ts:42, dentro de aplicarDescuento, y de golpe el error deja de ser ruido y se vuelve accionable. Y en el edge esto se resuelve sin el dilema clásico del navegador —exponer tu fuente para poder leerla—, porque el mapa vive en privado, del lado de Cloudflare.
- Entender por qué una traza de producción es ilegible tras la minificación de Wrangler.
- Saber qué es un source map y qué información contiene para deshacer esa minificación.
- Activar
upload_source_mapsenwrangler.jsoncy cómo Cloudflare remapea las excepciones. - Reconocer los límites y la propiedad clave: el mapa se guarda en privado, no se sirve al público.
Por qué la traza de producción es ilegible
Antes de subir tu Worker, Wrangler lo empaqueta y lo minifica con esbuild: reúne todos tus módulos en un único archivo, elimina espacios y comentarios, y renombra las variables a identificadores de una o dos letras para que el bundle pese lo mínimo. El resultado es un index.js de una sola línea gigantesca, funcionalmente idéntico a tu código pero visualmente irreconocible.
Cuando ese código lanza una excepción, la traza de pila describe posiciones de ese archivo generado, no del tuyo. Te roba justo lo que necesitas para actuar:
- El nombre real de la función donde falló, sustituido por una letra como
aot. - El archivo de origen, colapsado en un único bundle que reúne decenas de módulos.
- La línea y la columna con sentido, reducidas a la línea 1 y una columna de decenas de miles.
Sin traducción, depurar una excepción de producción es adivinar a ciegas.
Qué es un source map
Un source map es un archivo .map que acompaña al bundle y contiene la correspondencia exacta entre cada posición del código generado y su posición original: qué archivo, qué línea, qué columna y qué nombre tenía cada símbolo antes de la minificación. Con él, index.js:1:48210 se traduce sin ambigüedad a carrito.ts:42:8, aplicarDescuento. Es, literalmente, el diccionario que deshace la transformación del empaquetado.
Un .map con sourcesContent incluye el texto íntegro de tu código original —lógica, comentarios, nombres reveladores—. En el navegador esto crea un dilema conocido: publicarlo para poder leer las trazas equivale a servir tu fuente a cualquiera que abra las DevTools. Recuerda ese dilema, porque la gracia del enfoque de Cloudflare es precisamente que aquí no aparece.
Activar upload_source_maps
En Workers todo se reduce a una clave en el manifiesto:
{
"name": "mi-worker",
"main": "src/index.ts",
"compatibility_date": "2026-01-01",
"upload_source_maps": true,
"observability": { "enabled": true }
}
Con upload_source_maps en true, cada wrangler deploy genera los source maps del bundle y los sube a Cloudflare junto al código. No se sirven a los usuarios ni se exponen en ninguna URL pública: quedan almacenados del lado de la plataforma, con el único propósito de traducir trazas. A partir de ahí, cuando tu Worker lanza una excepción, Cloudflare usa el mapa correspondiente para remapear la traza y te la muestra ya legible en el panel de observabilidad, en Workers Logs y en wrangler tail.
# Genera y sube los source maps como parte del despliegue
wrangler deploy
# En observabilidad veras la traza remapeada:
# TypeError en src/carrito.ts:42:8 -> aplicarDescuento
flowchart LR Src[tu codigo carrito.ts] --> Build[wrangler deploy con esbuild] Build --> Min[bundle minificado index.js] Build --> Map[source map generado] Map -->|subido en privado| CF[almacen de Cloudflare] Min --> Prod[Worker en produccion] Prod -->|excepcion| Trace[traza cruda index.js 1 48210] Trace --> Remap[Cloudflare remapea con el mapa privado] CF --> Remap Remap --> Legible[traza legible carrito.ts 42 aplicarDescuento] style Map fill:#f9e2af,color:#11111b style CF fill:#cba6f7,color:#11111b style Legible fill:#a6e3a1,color:#11111b style Trace fill:#f38ba8,color:#11111b
Qué se remapea, y sus límites
El remapeo lo hace la plataforma en el momento de mostrarte la traza, no tu código en ejecución. Esto tiene dos consecuencias prácticas. La primera: para aprovecharlo, deja que las excepciones lleguen a la plataforma —bien porque se propagan sin capturar, bien porque las registras enteras— y lee la traza ya traducida en observabilidad, en lugar de intentar simbolizar a mano una cadena error.stack que en tiempo de ejecución sigue apuntando al bundle. La segunda: hay límites de tamaño para los source maps que subes (del orden de unos pocos megabytes comprimidos), así que Workers enormes pueden requerir dividir el código o revisar la configuración del bundle.
El coste de upload_source_maps es casi nulo —unos segundos más de despliegue y algo de almacenamiento privado— y el beneficio es enorme: la diferencia entre una guardia a las tres de la madrugada mirando a.js:1:48210 y otra leyendo carrito.ts:42 aplicarDescuento. No es una herramienta de lujo para cuando haya un problema; es infraestructura que quieres ya instalada antes del problema, porque el problema no avisa. Actívalo junto a observability y olvídate.
Todo despliegue moderno abre una brecha entre el código que escribes y el código que corre: transpilas, empaquetas, minificas, y lo que llega a producción es una traducción optimizada para la máquina, ilegible para el humano que la concibió. Esa brecha es el precio del rendimiento, y normalmente es un precio justo —hasta el instante en que algo se rompe y necesitas que el sistema te hable de vuelta—. Ahí se revela la verdad incómoda de la observabilidad: una telemetría solo vale lo que valga su capacidad de señalar tu modelo mental, no el de la máquina. Una traza que nombra aplicarDescuento en carrito.ts:42 cae directamente sobre el mapa que tienes en la cabeza de tu propio sistema; una que nombra a en index.js:1:48210 describe una realidad —la del bundle— que tú nunca pensaste y en la que no sabes moverte. El source map es, en el fondo, un traductor entre dos ontologías: la de la máquina, que optimiza, y la tuya, que razona. Y el detalle donde Cloudflare acierta con elegancia es dónde vive ese traductor. En el navegador, deshacer la minificación te obligaba a un dilema desagradable —publicar el mapa, y con él tu código, para poder leer las trazas—, de modo que la legibilidad se pagaba en exposición. En el edge el mapa se sube en privado y solo la plataforma lo usa, del lado servidor, para traducir lo que tú lees: obtienes la legibilidad sin pagar la exposición, porque el traductor nunca sale de casa. La lección que llevarte es doble. Una: instrumentar no basta si la instrumentación habla en el idioma equivocado; una traza minificada es tan inútil como no tener traza. Dos: las mejores herramientas de observabilidad no solo revelan lo que pasó, sino que lo revelan en los términos en que tú piensas el sistema, y lo hacen sin obligarte a elegir entre ver claro y estar seguro.
- En un Worker con TypeScript, provoca una excepción dentro de una función con nombre reconocible y despliega sin
upload_source_maps; observa la traza ilegible en observabilidad. - Añade
upload_source_maps: trueawrangler.jsonc, redespliega y compara: la misma excepción debería mostrar ahora archivo, línea y nombre de función reales. - Verifica por ti mismo que el
.mapno se sirve al público: comprueba que no aparece en ninguna URL de tu Worker. - Captura la excepción con un
try/catch, registra el error entero y comprueba cómo se ve la traza en Workers Logs frente a dejarla propagar. - Razona por qué el enfoque de Cloudflare —mapa privado remapeado del lado servidor— evita el dilema de exposición que sí tiene un source map servido en un navegador.