Ir al contenido

Errores

El motor responde los errores de dos maneras, y conviene no mezclarlas:

  • Un error HTTP dice que el pedido no se aceptó (o que no se pudo leer lo que pediste). Llega en la respuesta, con el formato de esta página.
  • Un comando fallido es un pedido que sí se aceptó (un 202) y después no tuvo efecto. Llega como command.failed o command.ambiguous y en GET /v1/commands/{id}, con error_code y stage. Está en Errores de un comando.

Todo error HTTP es un problem+json (RFC 9457), con Content-Type: application/problem+json:

Campo Qué es
type Solo en los errores tipados: urn:motor-whatsapp:error:<código>. Es estable: decide por este campo, no por el texto.
title El texto estándar del código HTTP (Unprocessable Entity, Conflict…).
status El código HTTP, repetido.
detail Una frase en español neutro, lista para mostrar a una persona. Puede cambiar de redacción: no la compares.
errors Opcional: una lista de { message, location } con el campo que falló (body.texto, query.limit, header.Idempotency-Key).
409 Conflict
{
"title": "Conflict",
"status": 409,
"detail": "Esa Idempotency-Key ya se usó con otro mensaje."
}
503 Service Unavailable (tipado)
{
"type": "urn:motor-whatsapp:error:multimedia_deshabilitada",
"title": "Service Unavailable",
"status": 503,
"detail": "La multimedia está deshabilitada: falta configurar el almacenamiento en Ajustes. El texto sigue funcionando."
}

Tres reglas del motor sobre sus errores:

  • Nunca devuelve lo que mandaste. Un error no repite el valor rechazado (value), así que una contraseña, un token o un secreto mal escritos no vuelven en la respuesta.
  • Un 404 no enumera dueños. Una instancia de otra app, o fuera de las instancias de tu key, es «No existe esa instancia.», igual que una que no existe.
  • Un 500 no da detalles. El motor registra la causa en su log y responde siempre «Algo falló en el motor. Intenta de nuevo.».

Cada respuesta trae además el encabezado X-Request-Id, que el motor genera y anota en su log. Si le reportas un error al operador, dale ese valor.

Un 422 de validación sale cuando el cuerpo, la query o un encabezado no cumplen el contrato (un campo obligatorio que falta, un texto demasiado largo, un número fuera de rango). Los cuerpos son cerrados: un campo que el contrato no tiene también es un 422.

detail nombra el primer campo que falló, con su motivo, y cuántos errores más hay; errors trae todos, cada uno con su location:

422 Unprocessable Entity
{
"title": "Unprocessable Entity",
"status": 422,
"detail": "Revisa los datos enviados: «texto» admite hasta 4096 caracteres (y otro error más).",
"errors": [
{ "message": "admite hasta 4096 caracteres", "location": "body.texto" },
{ "message": "no es un campo admitido", "location": "body.prioridad" }
]
}

Los motivos vienen en español. Los más frecuentes:

message Qué pasó
falta el campo obligatorio X Falta un campo requerido.
no es un campo admitido Mandaste un campo que el contrato no tiene (el cuerpo es cerrado).
admite hasta N caracteres · debe tener al menos N caracteres Un texto fuera de largo.
admite hasta N elementos · debe tener al menos N elementos Una lista fuera de tamaño.
debe ser mayor o igual que N · debe ser menor o igual que N Un número fuera de rango.
debe ser uno de "…" Un valor que no está en la enumeración.
debe ser un texto · debe ser un número entero · debe ser verdadero o falso Un tipo equivocado.
debe ser un UUID · debe ser una fecha y hora RFC 3339 Un formato equivocado.
no tiene el formato esperado (…) No cumple el patrón del campo.

Algunas reglas del dominio también responden 422 con su campo en errors (un destino con más de un tipo, un peer_ref de otra instancia, una ubicación fuera de rango) o con un código tipado (interactivo_invalido, vcard_invalida, formato_incompatible; ver Códigos tipados).

HTTP Cuándo Qué hacer
400 El cuerpo no es un JSON válido, falta el cuerpo obligatorio o una subida de archivo se cortó. Corrige el pedido. No lo repitas igual.
401 Falta Authorization: Bearer mw_…, o la key no es válida, venció, se revocó o su app está suspendida. El motor no dice cuál de esas. Revisa la key con el operador. No reintentes en bucle.
403 La key no tiene el scope de la operación; una key restringida intenta crear una instancia; un cursor de stream es de otra app; o WhatsApp no deja al número (sin_permiso). Pide el scope al operador o usa otra key.
404 No existe el recurso para tu app: tampoco si es de otra app o está fuera de las instancias de tu key. Revisa el ID.
409 El pedido choca con el estado: una Idempotency-Key reusada con otro contenido, la instancia sin vincular, archivada o frenada, una generacion_esperada o version_esperada vieja, un comando que ya no se puede cancelar. Relee el recurso y decide. No reintentes igual.
410 Algo que existió ya no está: un cursor de stream vencido (trae primer_cursor), un medio borrado o vencido, una invitación revocada. Ver la fila del código tipado.
413 El archivo supera el tamaño máximo de Ajustes, o el cuerpo JSON pasa de 1 MiB. Manda menos (o sube el archivo, no lo incrustes).
415 El Content-Type del cuerpo no es uno que el motor lea. Manda application/json.
422 Validación: ver El detalle de un 422. Corrige el campo.
429 Una cuota o un ritmo: cupo de instancias, cola de envíos llena, ritmo de envío, streams abiertos, consultas del día. Puede traer Retry-After. Ver Cuotas y límites.
500 Un error interno. Reintenta con backoff; si sigue, avisa al operador con el X-Request-Id.
501 El motor no implementa eso: atender llamadas (llamadas_no_soportadas), o el proveedor no consulta catálogos. No reintentes.
502 WhatsApp respondió algo que el motor no acepta (el catálogo, la passkey). Reintenta más tarde o pide menos.
503 El motor no puede atender ahora: arranca, se está reiniciando, este proceso está en espera, la base no responde al validar la key, o la multimedia no está configurada o no tiene disco. Algunos traen Retry-After. Reintenta con backoff, respetando Retry-After.
504 WhatsApp no contestó a tiempo una consulta (consulta_agotada). Nunca se entrega como una lista vacía. Reintenta más tarde.
Situación ¿Reintentar?
Se cortó la red y no sabes si el pedido llegó Sí, con la misma Idempotency-Key: recibes el mismo comando, nunca uno nuevo.
429 o 503 con Retry-After Sí, después de esperar lo que dice.
500, 502, 503, 504 sin Retry-After Sí, con backoff exponencial.
400, 401, 403, 404, 409, 410, 413, 415, 422 No: repetir lo mismo da lo mismo. Cambia el pedido o la credencial.
Ventana de terminal
# -i muestra el código y los encabezados (Retry-After, X-Request-Id).
curl -i -X POST https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/messages \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-1042-confirmacion" \
-d '{"destino": {"telefono": "+NUMERO_DEL_CONTACTO"}, "tipo": "text", "texto": "Hola"}'

Un error tipado trae type: urn:motor-whatsapp:error:<código>. El código es estable; el texto de detail, no. El SDK de Go lo expone en motor.Error.Codigo().

Código HTTP Cuándo Qué hacer
interactivo_invalido 422 Un interactivo no cumple sus límites; errors dice el campo. Corrige el campo indicado.
mencion_invalida 422 Una mención no es un teléfono internacional, un JID de usuario ni un peer_ref de la instancia. Corrige la mención.
vcard_invalida 422 Un contacto (vCard) no es válido. Corrige el contacto o manda solo teléfonos.
cita_ajena 422 La cita no es un mensaje de este chat en esta instancia. Cita un mensaje del mismo chat.
objetivo_desconocido 422 El mensaje a editar, borrar o reaccionar no existe en este chat. Revisa el objetivo.
mensaje_ajeno 422 Editar o borrar un mensaje que no mandó este número. Solo se editan o borran los propios.
fuera_de_ventana 422 Pasó la ventana de WhatsApp: 20 minutos para editar, 2 días y 12 horas para borrar para todos. No se puede: manda un mensaje nuevo.
no_editable 422 Solo se edita un texto que no se borró. —
no_reenviable 422 El motor no guardó el contenido de ese mensaje o su tipo no se reenvía. Manda el contenido de nuevo, sin reenviar.
formato_incompatible 422 El archivo no sirve para ese tipo de mensaje (imagen JPEG o PNG; vídeo MP4 o 3GP; audio OGG/Opus, MP3, M4A, AAC o AMR). Mándalo como document o conviértelo.
formato_no_convertible 422 El archivo no se puede convertir al subtipo pedido (ptt, sticker, ptv, gif). Usa una fuente compatible.
url_no_permitida 422 El host de medio.url (o de la vista previa con obtener) no está en los destinos permitidos. Pide al operador que agregue el host, o sube el archivo.
audiencia_invalida 422 La audiencia de un status no es válida. Revisa privacidad, destinos y fondo.
audiencia_maxima 422 max_destinatarios pasa el máximo de Ajustes. Baja el número.
no_en_status 422 Un status no lleva cita, menciones ni vista previa. Quítalas.
tipo_no_admitido_en_canal 422 En un canal solo se publica texto, imagen, vídeo o nota de voz, sin cita ni menciones. Cambia el tipo.
medio_no_encontrado 404 Un media_id (del envío, de la miniatura de una vista previa o de una tarjeta de carrusel) no existe en esta instancia; la respuesta es la misma si es de otra instancia o de otra app. El campo va en errors[].location. Sube el archivo a esta instancia y usa su media_id.
no_listo 409 El media_id todavía no está listo. Espera media.ready y repite.
conversion_no_disponible 503 Este motor no tiene las herramientas de conversión. Manda el archivo como audio, imagen o vídeo.
Código HTTP Cuándo Qué hacer
multimedia_deshabilitada 503 Falta configurar el almacenamiento en Ajustes. El texto sigue funcionando. Avisa al operador.
almacen_no_disponible 503 El almacenamiento no responde. Trae Retry-After: 30. Reintenta después.
presion_disco 503 El disco temporal del motor está casi lleno. Trae Retry-After: 60. Reintenta después.
demasiado_grande 413 El archivo supera el tamaño máximo de Ajustes. Manda uno más chico.
demasiado_grande_para_base64 422 Pediste en base64 un archivo más grande que el máximo de base64. Descárgalo en binario.
vacio 422 El archivo está vacío. —
mime_falso 422 El Content-Type declarado no coincide con el contenido. Declara el tipo real.
digest 422 El SHA-256 declarado no coincide. Revisa el archivo o el hash.
cuota_excedida 429 Se superó la cuota de multimedia de la instancia o de la app. Espera a que venza la retención o pide más cuota.
no_disponible 410 El medio falló, se borró, venció su retención o su contenido no está. Vuelve a subirlo.
en_uso 409 Un envío pendiente usa el medio: no se borra hasta que salga o se cancele. Espera o cancela el envío.
Código HTTP Cuándo Qué hacer
cuota_agotada 429 La app usó sus consultas de identidad (o comerciales) del día. Vuelve a cero a las 00:00 UTC; trae Retry-After hasta entonces. Espera; no se consultó nada.
limitado_por_whatsapp 429 WhatsApp limitó las consultas de este número. Trae Retry-After. Espera antes de volver a consultar.
no_conectada 409 La instancia no está conectada y la operación es en el momento (consultas, presencia): no queda en cola. Espera a connected y repite.
identidad_desconocida 404 La instancia no conoce esa identidad (el motor no deduce un teléfono de un LID). Usa un teléfono o un peer_ref de la instancia.
Código HTTP Cuándo Qué hacer
sin_permiso 403 El número no es administrador del grupo o del canal. —
no_es_miembro 409 El número no está en ese grupo. —
sin_vincular 409 La instancia no está vinculada. Vincúlala.
sin_enlace 409 El motor no tiene un enlace de invitación del grupo. Pídelo primero (group.invite_link).
grupo_no_encontrado 404 WhatsApp dice que el grupo no existe. —
canal_no_encontrado 404 WhatsApp dice que el canal (o la invitación) no existe. —
invitacion_invalida 404 La invitación no es válida. —
invitacion_revocada 410 La invitación fue revocada o venció. Pide una nueva.
no_es_comunidad 422 Ese grupo no es una comunidad. —
foto_invalida 422 La foto no es un JPEG listo de esta instancia, de hasta 2 MB y de 192 a 2048 píxeles por lado. Sube una que cumpla.
Código HTTP Cuándo Qué hacer
tope_fijados 409 Ya hay tres chats fijados, el máximo de WhatsApp. Desfija uno.
etiqueta_desconocida 404 La etiqueta no existe en la instancia (o se eliminó). —
confirmacion_requerida 422 Borrar un chat no se deshace: falta confirmar_irreversible: true. Confírmalo.
importacion_apagada 409 La importación del historial está apagada en la instancia. Enciende config.historial_importar.
sin_mensajes 409 El motor no conoce ningún mensaje del chat para pedir historial. —
trabajo_terminado 409 El trabajo de historial ya terminó. —
interactivo_desconocido 404 Ese provider_id no es un interactivo de la instancia. —
llamadas_no_soportadas 501 El motor no atiende ni hace llamadas. Puedes rechazarlas y recibir sus señales.
Código HTTP Cuándo Qué hacer
cuota_agotada 429 La app usó sus consultas comerciales del día. Espera a las 00:00 UTC.
vistas_agotadas 429 La instancia llenó su cupo de resultados guardados. Trae Retry-After: 60. Espera a que venzan.
limitado_por_whatsapp 429 WhatsApp limitó las consultas; la instancia se enfría. Espera el Retry-After.
cursor_invalido 422 El cursor no es uno que dio el motor. Usa next_cursor tal cual.
cursor_vencido 409 El cursor venció. Pide la primera página.
cursor_incompatible 409 El cursor es de otra consulta, o el número se volvió a vincular. Pide la primera página.
observador_cambio 409 La instancia cambió de número durante la consulta: no se entrega el resultado. Repite si corresponde.
credencial_no_vigente 403 La key dejó de autorizar la consulta mientras corría. Revisa la key.
catalogo_no_autorizado 403 WhatsApp no dejó ver ese catálogo (no significa que no sea Business). —
catalogo_no_disponible 404 WhatsApp no encontró un catálogo para ese número. —
no_conectada 409 La instancia se desconectó durante la consulta. Repite al reconectar.
consulta_proveedor_rechazada 502 WhatsApp rechazó la consulta. —
respuesta_proveedor_invalida 502 La respuesta de WhatsApp no tiene el formato esperado. Reintenta más tarde.
respuesta_proveedor_demasiado_grande 502 La respuesta supera los límites del motor (nada se recorta). Pide menos con limit.
continuacion_sin_avance 502 WhatsApp devolvió una continuación ya usada. Vuelve a la primera página.
proveedor_temporalmente_no_disponible 503 WhatsApp no está disponible para la consulta. Trae Retry-After. Reintenta después.
registro_no_disponible 503 La consulta terminó, pero el motor no pudo registrar el resultado. Trae Retry-After: 30. Reintenta.
consulta_agotada 504 WhatsApp no contestó a tiempo. Reintenta más tarde.
catalogo_no_soportado 501 El proveedor no consulta catálogos. —
doc_id_catalogo_invalido · variables_catalogo_invalidas · via_catalogo_sin_token 409 La configuración del catálogo en Ajustes no sirve. No se consultó nada. Avisa al operador.

En el catálogo, un error de WhatsApp trae en errors el consulta_id de su evento query.failed.

Un comando aceptado (202) que termina sin efecto queda fallido; uno que pudo salir sin confirmación queda ambiguo. Los dos traen error_code; los envíos traen además stage. Lo explica Comandos asíncronos y eventos.

stage Qué significa ¿Salió algo?
previa Falló antes de mandarlo. No. Puedes reenviarlo con una Idempotency-Key nueva.
servidor WhatsApp lo rechazó. Trae server_code (por ejemplo, 400) y server_origin (iq o mex). No.
frontera Se cortó justo al mandarlo: queda ambiguo. Quizás. No lo reenvíes a ciegas.

Mientras un envío espera un reintento (en_cola), error_code y stage dicen el motivo del último intento; solo valen como definitivos en fallido o ambiguo. Los comandos de control (grupos, perfil, etiquetas, lectura, operaciones de la instancia) no traen stage.

Antes de ejecutar, el motor revalida quién aceptó el comando y la instancia. Si algo cambió, el comando falla sin salir.

error_code Qué pasó Qué hacer
credencial_revocada La key que lo pidió se revocó (también por rotación). Repite con la key nueva y una clave nueva.
credencial_vencida La key venció. Ídem.
app_suspendida La app está suspendida. Habla con el operador.
sin_scope La key perdió el scope del comando. Pide el scope.
sin_acceso_a_la_instancia La key ya no alcanza a la instancia. Pide el acceso.
operador_inactivo Lo pidió un operador que ya no está activo. —
comando_vencido Pasó su expires_at antes de salir. Decide si todavía vale y manda uno nuevo.
instancia_archivada La instancia está archivada. —
sin_vincular La instancia no está vinculada. Vincúlala y repite.
generacion_cambiada La instancia se volvió a vincular después de aceptarlo: no sale por el número nuevo. Decide y manda uno nuevo.
sin_instancia · tipo_desconocido El comando no indica una instancia, o el motor no conoce el tipo. —
error_code Estado · stage Qué pasó Qué hacer
destino_invalido fallido · previa El destino no se pudo resolver o no es válido. Corrige el destino.
sin_permiso fallido · previa El número dejó de administrar el canal destino. —
medio_no_disponible fallido · previa El archivo del envío falló, se borró o venció. Súbelo de nuevo.
medio_incompatible fallido · previa El archivo no sirve para ese tipo de mensaje. Conviértelo o mándalo como documento.
conversion_fallida fallido · previa Falló la conversión (media.motivo: formato_no_convertible, tiempo_excedido o memoria_excedida). Revisa el archivo.
objetivo_no_disponible fallido · previa El mensaje a editar, borrar o reenviar ya no está disponible. —
fuera_de_ventana fallido · previa Al salir, ya pasó la ventana de edición o de borrado. —
no_reenviable · reenvio_ilegible fallido · previa El contenido a reenviar no se reenvía o no se puede leer. Manda el contenido de nuevo.
audiencia_excedida · fuera_de_privacidad · privacidad_distinta · sin_destinatarios · privacidad_desconocida · sin_cifrar fallido · previa Un status cuya audiencia no se cumple. Revisa la audiencia.
no_salio fallido · previa Agotó los reintentos sin poder mandarlo (sin sesión, una subida que falla). Reenvíalo cuando la instancia esté conectada, con una clave nueva.
rechazado_por_whatsapp fallido · servidor WhatsApp lo rechazó (server_code). No reintentes igual.
sin_respuesta ambiguo · frontera Salió y no hubo respuesta del servidor. Espera la conciliación.
incierto ambiguo · frontera Error al mandarlo, sin saber si salió. Espera la conciliación.
reinicio_en_la_frontera ambiguo · frontera El motor se reinició mientras lo mandaba. Espera la conciliación.

Un ambiguo se concilia solo si llega su eco o su recibo: pasa a enviado y llega un message.sent con conciliado: true. Reenviarlo con otra Idempotency-Key puede duplicar el mensaje.

error_code Qué pasó
rechazado_por_whatsapp WhatsApp rechazó el cambio (server_code y server_origin, si los dio).
sin_permiso · no_es_miembro · ya_es_miembro El número no administra el grupo, no está en él, o ya estaba al unirse.
grupo_no_encontrado · canal_no_encontrado WhatsApp dice que no existe.
invitacion_invalida · invitacion_revocada La invitación no sirve.
resultado_incierto Crear un grupo, una comunidad o un canal no respondió: pudo crearse. Queda ambiguo.
foto_invalida · sin_identidad · info_largo El perfil: la foto no sirve, falta la identidad o el «Info» pasa el largo.
sin_ultimo_mensaje · objetivo_desconocido · etiqueta_desconocida · tope_fijados Archivar, marcar, fijar o etiquetar un chat: falta el último mensaje, el mensaje o la etiqueta no existen, o ya hay tres fijados.
sin_mensajes · no_reproducible Marcar leído un chat sin mensajes conocidos, o marcar reproducido algo que no es audio ni vídeo.
conexion_fallida · desvinculo_fallido Reconectar o desvincular la instancia no funcionó.
sin_sesion · operacion_fallida · clave_faltante · appstate_fallido · no_capturado · limitado_por_whatsapp Errores pasajeros: el comando vuelve a la cola con backoff. Si agota los intentos, queda fallido con el último.

Para saber qué evento cuenta el efecto de cada comando, mira el Catálogo de eventos.