Skip to main content

Debugging de mismatch preview-vs-send

El email rendereó bien en el preview de Email Studio, salió, y la versión enviada real es distinta. Seis checkpoints que reproducen la divergencia determinísticamente antes de que dispare el próximo send — variables de contexto, gates de _messagecontext, formateo de locale, lógica time-sensitive, drift de data de Lookup, y el único diagnóstico verdaderamente concluyente: un test send real.

Cómo hacerlo·Actualizado 2026-05-19·Escrito por Lira · Editado por German Medina

El email rendereó bien en el preview de Email Studio. QA aprobó. El Send salió. Los emails enviados reales se ven distintos — un content block apareció (o no), un timestamp se movió, un precio se formateó distinto, una personalización cayó a un default cuando no debería. La Send Activity reporta Completed. No hay error. La próxima persona del equipo pregunta cómo el preview puede no estar de acuerdo con el send cuando "es el mismo template" — y vos tenés que explicar que preview y send usan code paths distintos, y la única forma de saber qué path tomó tu script es recorrer las diferencias explícitamente.

Esta página es el playbook de diagnóstico para esa forma exacta. Seis checkpoints que reproducen la divergencia antes de que dispare el próximo send, más la nota de cierre de que la única verificación verdaderamente concluyente es un test send real a una Business Unit de staging. Ver gotchas — #9.

Lo que preview y send no comparten

[ Preview de Email Studio ]            [ Send de Producción ]
        ↓                                       ↓
_messagecontext = "Preview"           _messagecontext = "Send"
_jobid          = NULL                _jobid          = (JobID real)
_emailaddress   = test sub elegido    _emailaddress   = destinatario real
Now()           = render del preview  Now()           = momento del send
locale          = tenant del user MC  locale          = perfil del subscriber
data de Lookup  = DE en preview-time  data de Lookup  = DE en send-time
Cloud-write     = a veces bloqueado   Cloud-write     = siempre dispara (live)

Cada fila de arriba es un lugar donde el output rendereado puede divergir. Los checkpoints abajo recorren cada uno en orden de más-a-menos común como falla de producción real.

Paso 1 — Encontrá cada gate _messagecontext en el cuerpo del email

El mismatch preview-vs-send más común: un content block (o una variable seteada por AMPscript) se gateó sobre _messagecontext == "Send". En preview, el gate evaluó false, el bloque no rendereó, y el reviewer de QA pensó que el contenido controlado por el gate se suponía oculto. En send, el gate evaluó true, el bloque rendereó, y el email rendereado sorprendió a todos.

%%[
  IF Lowercase(_messagecontext) == "send" THEN
    /* Este bloque es INVISIBLE en preview pero VISIBLE en send.
       Si el reviewer de QA no sabía que existía, firmó un preview
       "lo que ves es lo que sale" que no lo era. */
    SET @giftCode = ClaimRow("de_gift_codes", ...)
  ENDIF
]%%

Grepeá el cuerpo del email y cualquier Code Resource incluido por _messagecontext y reviá cada match. La convención Cleon: documentá los bloques con gate en el comentario de frontmatter del email (un comentario al tope del cuerpo listando cada bloque gateado con _messagecontext) para que el próximo reviewer sepa qué buscar.

Paso 2 — Encontrá cada referencia a _jobid

_jobid es NULL en preview, un ID real en send. Cualquier AMPscript que ramifica sobre _jobid o lo usa como parte de una key de lookup cambia de forma entre los dos contextos.

%%[
  /* Este Lookup keyeado sobre _jobid no devuelve nada en preview (_jobid
     es NULL) y devuelve la fila real de send-time en send. El bloque que
     depende del resultado del lookup renderea distinto. */
  SET @sendConfig = Lookup("de_send_configs", "Subject", "JobID", _jobid)
]%%

Buscá en el script _jobid y verificá que cada uso o:

  • Tiene un fallback con Empty() que maneja el caso NULL-en-preview, o
  • Está adentro de un gate _messagecontext == "Send", o
  • Es read-only y su valor NULL no va a cambiar el output rendereado.

Referencias peladas a _jobid sin una de esas tres protecciones causan divergencia preview-vs-send.

Paso 3 — Encontrá cada cómputo time-sensitive

Now() devuelve el momento del render. Preview a las 11am y send a las 9pm dan valores muy distintos. Cualquier cosa que ramifica sobre la hora actual, o interpola un string de tiempo relativo ("te quedan 3 horas"), o usa comparación de fecha contra Now(), diverge si preview y send pasan en momentos distintos.

%%[
  /* EN RIESGO — "Hoy es el último día de la oferta!" puede renderear true
     en el preview de las 11am y false en el send de las 9pm (o viceversa,
     alrededor de bordes de medianoche). */
  SET @daysLeft = DateDiff(Now(), @saleEnds, "Day")
  IF @daysLeft == 0 THEN
    SET @subject = "Último día!"
  ENDIF
]%%

Auditá Now(), SystemDate(), LocalDate(), DateAdd(Now(), ...), DateDiff(..., Now(), ...), y cualquier output de string que incluya un tiempo relativo. Para cada match:

  • Confirmá que el valor sería el mismo en preview y en el send time real
  • Si no lo sería, o pre-computá el valor time-sensitive upstream en una SQL Activity (para que quede fijo en audience-build time), o aceptá que el preview no es autoritativo para contenido time-sensitive y requerí un test send real cerca del horario del send de producción.

Paso 4 — Encontrá cada función de formato locale-aware

FormatDate(d, fmt, locale) y FormatNumber(n, fmt, locale) usan el argumento locale. Si el locale viene del perfil del destinatario y el destinatario (test subscriber de preview vs destinatario real) tienen valores de locale distintos, el output rendereado diverge en silencio.

-- Comparar locales entre el test subscriber usado para preview y una muestra
-- de destinatarios reales. Si difieren, el formato rendereado difiere.
SELECT
  Locale,
  COUNT(*) AS Subscribers
FROM de_send_audience_<send_name>
GROUP BY Locale
ORDER BY Subscribers DESC;

Si la audiencia tiene múltiples locales pero el preview de QA solo testeó uno, los destinatarios en los otros locales ven formato distinto. El fix es upstream: la SQL de audience-build debería alinear el locale por destinatario, y el proceso de QA debería previewar explícitamente al menos un destinatario por locale distinto en la audiencia.

Paso 5 — Comparar data de Lookup entre preview-time y send-time

Los DEs que tu AMPscript lee vía Lookup / LookupRows cambian entre preview-time y send-time. Una fila que existía en el DE de segments cuando QA previewó a las 3pm puede haber sido actualizada o borrada por una SQL Activity de rebuild de las 5pm antes del send de las 9pm.

-- Verificá que la fila existe para el destinatario afectado en el momento
-- que estás mirando. Corré esto dos veces — una ahora (antes del send),
-- una después del send — y comparalas.
SELECT
  SubscriberKey,
  Tier,
  UpdatedDate
FROM master_segments
WHERE SubscriberKey = 'abc-12345';

La falla del hand-off: una SQL Activity nocturna reconstruye master_segments entre el preview de QA y el send. El valor de Tier cambia; el Lookup devuelve un resultado distinto en send time. El fix no es del lado de AMPscript — es coordinar el audience-build y el send-time para que lean del mismo snapshot. Auditá el orden de los pasos de la Automation: la SQL Activity que reconstruye el DE de lookup debería correr antes del audience-build que usa la Send Activity, no después.

Paso 6 — La única verificación concluyente: un test send real

El preview te deja iterar rápido. No verifica el comportamiento de send-time end-to-end. La única verificación concluyente es un test send real a un subscriber real en una Business Unit de staging, corrido en un horario cerca del horario planeado del send de producción.

Procedimiento de test send (convención Cleon):

1. Crear una Business Unit de staging (o reusar una existente).
2. Crear un test subscriber ahí con data realista — no un placeholder
   sintético. El locale del test subscriber, el perfil de AttributeValue,
   y la fila del DE sendable deberían espejar a un destinatario real
   de producción.
3. Mandar el email a la audiencia de la BU de staging (tamaño 1: el
   test subscriber).
4. Abrir el email efectivamente entregado en la inbox del test subscriber.
5. Compará contra el preview. Cualquier divergencia es un bug a investigar
   antes de que el send de producción dispare.
6. Si el send de producción está scheduleado para un horario específico,
   corré el test send dentro de la hora de ese horario (especialmente
   si el script tiene cualquier lógica time-sensitive).

El test send corre el mismo renderer que el send de producción. _messagecontext == "Send", _jobid es real, todos los gates disparan, el locale resuelve según el test subscriber, la lógica time-sensitive usa el momento del send real. Cualquier cosa que diverja en el test send también va a divergir en producción — y la atrapás en la audiencia de staging de uno, no en 50.000 inboxes de producción.

Divergencias comunes rankeadas por frecuencia

| Divergencia | Dónde aparece | Fix | |---|---|---| | Bloque gateado por _messagecontext oculto en preview, visible en send | Un content block aparece en inbox que QA no esperaba | Paso 1: documentar bloques gateados; preview-validar el estado del contexto de send | | _jobid Lookup devuelve NULL en preview, valor real en send | Condicional renderea distinto según el send config | Paso 2: fallback con Empty() o gate _messagecontext alrededor del uso de _jobid | | Lógica basada en Now() se corre entre preview y send time | Subject lines time-sensitive, countdowns, ramas "hoy es X" | Paso 3: pre-computar upstream en SQL, o aceptar que preview no es autoritativo | | Formato de locale difiere entre test subscriber y destinatarios reales | Números/fechas renderean en formato inesperado para algunos segmentos | Paso 4: auditar la distribución de locales de la audiencia; previewar múltiples locales | | El DE de Lookup cambió entre el momento del preview y el del send | Valor de tier/segmento renderea distinto a lo que QA esperaba | Paso 5: auditar el orden de pasos de la Automation; rebuild del DE de lookup antes del audience-build | | Cloud-write dispara en send pero no en preview | Updates de CRM aterrizan que QA no vio en preview | Paso 1 (gates) + test send + auditar de_log_sf_writes después del primer send | | Subscriber Attribute populado para test subscriber pero faltante para destinatarios reales | Una personalización de profile-attribute anda para QA, falla para algunos destinatarios | Paso 4 (auditar cobertura del perfil); agregar default Empty() |

Relacionado