Ingeniería de Software9 min de lectura22 de julio de 2026

Error 463 de WhatsApp: cuando la API dice «enviado» y el mensaje nunca llega

Una noche entera descartando hipótesis para acabar encontrando un bug de la librería, invisible desde la API. Así se diagnostica un fallo que no deja rastro en ningún log.

Por Jordan Moyano · 22 de julio de 2026

Migramos un bot de WhatsApp a un servidor nuevo. Todo arrancó bien: la sesión se vinculó por QR, la API respondía, los mensajes entrantes llegaban al backend y se procesaban. Solo había un detalle. Los mensajes salientes no llegaban a nadie.

Y no fallaban. Eso es lo peor. La API los aceptaba, les asignaba un identificador real y devolvía status: PENDING, exactamente igual que cuando funcionaba. Desde fuera, el sistema decía que todo iba bien. En el móvil del destinatario no aparecía nada.

Por qué este tipo de fallo es tan caro de diagnosticar

Un error normal te da algo con lo que trabajar: un código, una excepción, una línea en un log. Este no daba nada. La capa que usábamos —Evolution API, que envuelve la librería Baileys— consideraba que su trabajo terminaba al entregar el mensaje al socket. Lo que WhatsApp respondía después nunca subía hasta la respuesta HTTP.

El resultado es un sistema que miente sin saberlo. Y cuando el síntoma es «no pasa nada», la tentación es asumir que el problema está en tu infraestructura, que es lo último que has tocado. Nos costó una noche entera descubrir que no.

Lo que descartamos (y por qué importa contarlo)

Antes de encontrar la causa real probamos once hipótesis. Ninguna era correcta, pero cada una eliminaba una capa entera del sistema. Esta es la parte que casi nunca se publica y que, si te pasa lo mismo, es la que te ahorra la noche:

  • Dispositivo recién vinculado, sin «confianza» de WhatsApp — descartado con una sesión que la propia app confirmaba como activa, no solo conectada según la API.
  • Un destinatario concreto — falló igual con dos números distintos.
  • Configuración distinta a la del servidor viejo — comparamos las variables de entorno completas, el digest de la imagen y los ajustes de instancia. Idénticos.
  • Colisión entre dos dispositivos vinculados al mismo número — falló igual con el sistema antiguo completamente apagado.
  • Sincronización de historial incompleta — forzamos la sincronización total, se completó de verdad, y siguió fallando.
  • Versión desactualizada de la API — probamos una versión posterior. Mismo resultado. En retrospectiva, esta fue la pista más valiosa y la interpretamos mal.
  • Reloj desincronizado — NTP correcto en las cuatro máquinas, menos de un segundo de diferencia.
  • MTU distinto entre capas de red — 1500 en todas.
  • Entropía insuficiente, el clásico de las máquinas virtuales recién creadas — idéntica en ambos hosts.
  • Corrupción de checksum de red, un fallo conocido en entornos virtualizados — sin evidencia: esa misma máquina había movido cientos de megabytes esa noche sin un solo error.
  • Cortafuegos del hipervisor — desactivado y verificado.

Once capas eliminadas con evidencia real, no por intuición. Y el mensaje seguía sin llegar.

El punto de inflexión: mirar el protocolo, no la API

El cambio de estrategia fue dejar de preguntarle a la API qué creía que estaba pasando y ponerse a escuchar lo que WhatsApp respondía de verdad. Baileys tiene una variable de entorno que activa la traza cruda del protocolo:

LOG_BAILEYS=trace

La primera respuesta que apareció con esa traza activada resolvió el caso en un segundo:

<ack from='[email protected]'
     class='message'
     id='...'
     error='463' />

"msg":"received error in ack"

Ahí estaba. Un error explícito, numerado, que WhatsApp llevaba devolviendo toda la noche en cada intento — y que ninguna capa por encima mostraba jamás.

Qué es el error 463

El código 463 corresponde a NackCallerReachoutTimelocked. Traducido: WhatsApp ha decidido que estás contactando a alguien que no te conoce, y aplica un bloqueo temporal de envíos.

La causa concreta es que faltaba un token de privacidad —tctoken— que el cliente debe adjuntar a los mensajes individuales. Sin él, la plataforma no puede verificar que exista una relación previa entre los dos números, y trata el envío como si fuera contacto en frío. El mensaje se acepta, se encola y se descarta silenciosamente.

Por qué la instalación antigua no lo sufría

Esta era la pieza que nos tenía dando vueltas. El sistema viejo funcionaba perfectamente con la misma versión de todo. La explicación: su sesión llevaba conectada desde hacía casi un mes, establecida antes de que este comportamiento empezara a aplicarse. El bloqueo solo golpea a las sesiones nuevas.

De ahí la trampa: cualquier comparación entre el sistema viejo y el nuevo daba «idéntico», porque la diferencia relevante no estaba en la configuración. Estaba en la antigüedad de la sesión, que no aparece en ningún fichero.

El arreglo

El fix ya existía en la librería. Se fusionó en Baileys en abril de 2026 y salió publicado por primera vez en la versión 7.0.0-rc10. El problema es que Evolution API 2.3.7 —y también la 2.4.0 en preview— empaquetan 7.0.0-rc.9, de noviembre de 2025. Justo la anterior.

Eso explica por qué actualizar la API no cambió nada: las dos versiones traían exactamente el mismo Baileys. Estábamos actualizando la capa equivocada.

La solución fue construir una imagen propia partiendo del código en su etiqueta estable, subiendo únicamente la dependencia:

git clone --depth 1 --branch 2.3.7 \
  https://github.com/EvolutionAPI/evolution-api.git

npm pkg set dependencies.baileys=7.0.0-rc13
rm -f package-lock.json
npm install --package-lock-only --ignore-scripts

Dos detalles que cuestan tiempo si no los sabes. El primero: hay que regenerar el lockfile, o la compilación aborta por desajuste con el package.json. El segundo: --ignore-scripts es obligatorio, porque el script de preparación del proyecto falla fuera de un repositorio git.

Subir la dependencia destapó además tres errores de tipado preexistentes, los tres el mismo patrón — una cabecera HTTP que ya no es asignable a string:

- mimetype = response.headers['content-type'];
+ mimetype = response.headers['content-type'] as string;

Con la imagen reconstruida y una sesión nueva vinculada, la traza mostró el tctoken presente en el mensaje saliente y un acuse de recibo limpio, sin el 463. El mensaje llegó al móvil.

Lo que probamos y no funcionó

Antes de reconstruir la imagen intentamos algo aparentemente más barato: trasplantar una sesión ya vinculada y funcionando desde otra máquina, copiando sus filas de base de datos y su estado en caché. Los datos se copiaron correctamente —cientos de campos verificados— pero al arrancar, la librería no reconoció aquello como una sesión válida e inició un registro nuevo.

Merece la pena saberlo antes de intentarlo: las sesiones de WhatsApp no son datos portables. Cortamos a tiempo, sin dañar la original, pero la vía no existe.

Las tres lecciones que nos llevamos

  • Cuando el síntoma es silencio, baja de capa. Once hipótesis sobre infraestructura y ninguna sobre el protocolo. La traza cruda debería haber sido el primer paso, no el duodécimo.
  • «Actualicé y sigue igual» no significa que la versión no importe. Significa que quizá actualizaste la capa equivocada. Comprueba qué versión de la dependencia real empaqueta cada release.
  • Una imagen que solo existe en un servidor es deuda técnica. Ese build propio quedó documentado con su Dockerfile y sus parches, reproducible con un solo comando. Una imagen que no sabes reconstruir es una que perderás justo el día que la necesites.

Si te está pasando ahora mismo

  • Activa la traza del protocolo antes de tocar nada más. Si ves error='463' en el acuse de recibo, es esto y no tu red.
  • Comprueba qué versión de Baileys empaqueta tu release, no la versión de la API que la envuelve. Necesitas 7.0.0-rc10 o superior.
  • Deja la sesión en reposo unas horas si has hecho muchas pruebas. El bloqueo es temporal, no un veto permanente, pero los intentos repetidos lo alargan.
  • No intentes migrar una sesión existente. Vincula un dispositivo nuevo — WhatsApp permite varios por número y evitas arriesgar el que ya funciona.

Y desactiva la traza en cuanto termines. Genera un volumen de logs que no quieres en producción.

¿Tu bot dice que envió mensajes que nunca llegaron?

En NanoRed Labs diseñamos y mantenemos integraciones de WhatsApp que no fallan en silencio — con trazabilidad real de cada mensaje y sistemas reproducibles de arriba abajo. Cuéntanos qué estás viendo y te decimos por dónde empezar.

Solicitar diagnóstico técnico →
WhatsAppBaileysEvolution APIerror 463debuggingNode.jsDockerdiagnóstico
← Ver todos los artículos