Skip to main content

Debugging de blancos en render time

Un email salió, los destinatarios ven 'Hola, ' con un blanco donde debería ir el primer nombre. El Send muestra Completed. El Lookup de AMPscript devolvió NULL sin tirar. Cinco queries contra el DE de audiencia y cualquier DE de log que encuentran cuál de los tres culpables habituales es el bug realmente.

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

Un email salió. Los destinatarios ven Hola, (blanco donde debería ir el primer nombre). O Tu tier: (blanco donde debería ir el segmento). O un content block que tenía que renderearse condicionalmente directamente falta. La Send Activity reporta Completed. No hay error en ningún log porque Lookup y AttributeValue devuelven NULL sin tirar — ver gotchas — #2.

Esta es la forma de falla más común de AMPscript, y casi siempre es una de tres cosas: una columna faltante del DE de audiencia que el email esperaba leer; un Lookup contra un DE distinto donde el destinatario no tiene fila; o un Subscriber Attribute confundido con una columna de DE. Las cinco queries abajo separan los tres.

El flujo de render time

[ La Send Activity dispara JobID N contra el DE de audiencia A ]
        ↓ para cada fila en A...
[ AMPscript lee %%[Column]%% de la fila, AttributeValue() del perfil, Lookup() de otros DEs ]
        ↓ cada read o devuelve un valor o NULL silenciosamente
[ Email rendereado + enviado — los blancos aterrizan en el cuerpo donde los reads devolvieron NULL ]

El diagnóstico recorre cada flecha. El paso 1 confirma que la fila estaba en la audiencia. Los pasos 2-4 chequean cada mecanismo de read. El paso 5 captura el postmortem.

Paso 1 — ¿El destinatario estaba en la audiencia?

Si un destinatario específico reporta un blanco, arrancá confirmando que efectivamente estaba en el DE de audiencia para ese JobID.

-- Reemplazá 'de_send_audience_<send_name>' con el DE sendable real
-- Reemplazá el SubscriberKey con la key del destinatario afectado
SELECT
  SubscriberKey,
  EmailAddress,
  FirstName,
  Tier,
  COUNT(*) AS RowsForThisSub
FROM de_send_audience_<send_name>
WHERE SubscriberKey = 'abc-12345'
GROUP BY SubscriberKey, EmailAddress, FirstName, Tier;

Tres formas de falla acá:

  • Sin filas devueltas: el destinatario no estaba en la audiencia para este JobID. Recibió el email igual vía un Send distinto (fan-out cross-Send, sharing de BU). El AMPscript corrió contra una fila de audiencia distinta a la que pensás.
  • Una fila, pero FirstName es NULL: el DE de audiencia en sí no tiene el valor. Seguí al paso 2.
  • Una fila, todos los valores populados: el DE de audiencia tiene el data, pero el AMPscript no lo leyó. El bug está en cómo el script referencia el valor — seguí al paso 4.

Paso 2 — ¿El DE de audiencia tenía la columna populada?

Si la fila afectada muestra NULL en la columna que el email intentó renderear, la SQL Activity de audience-build es la fuente del bug. Contá las tasas de NULL a lo largo de toda la audiencia.

SELECT
  COUNT(*) AS TotalRows,
  COUNT(FirstName) AS RowsWithFirstName,
  COUNT(*) - COUNT(FirstName) AS RowsMissingFirstName,
  CAST((COUNT(*) - COUNT(FirstName)) * 100.0 / COUNT(*) AS DECIMAL(5,2)) AS PctMissing
FROM de_send_audience_<send_name>;

Si PctMissing supera unos pocos por ciento, la SQL de audience-build está dejando la columna NULL para una porción significativa de destinatarios. El default del AMPscript (IF Empty(@firstName) THEN ... = "Friend" ENDIF) es lo que debería atrapar esto — si no lo está haciendo, al AMPscript se le pasó la regla de defaulting del Style Guide.

El fix está en dos lugares:

  1. AMPscript: agregá el default de Empty() para que el próximo send no rendereaba en blanco.
  2. SQL Activity upstream: investigá por qué la columna es NULL para esas filas. Usualmente un LEFT JOIN que debería haber sido INNER JOIN, o un default faltante en el data fuente.

Paso 3 — ¿El script loggeó un resultado de Lookup NULL?

Si el AMPscript sigue el Style Guide y loggea reads a un de_log_ampscript_renders (o similar) DE, queryalo por el JobID + destinatario afectado.

-- Esto asume que el script escribió filas como:
--   InsertData("de_log_ampscript_renders",
--     "JobID", _jobid, "SubscriberKey", _subscriberKey,
--     "Step", "lookup-segment", "Value", @tier, "Ts", Now())
SELECT
  Step,
  Value,
  Ts
FROM de_log_ampscript_renders
WHERE JobID = 'JOB-2026-05-19-01'
  AND SubscriberKey = 'abc-12345'
ORDER BY Ts;

Qué te dicen las filas:

  • Una fila con Step = 'lookup-segment' y Value vacío/NULL confirma que el Lookup no devolvió nada para este destinatario. El bug está en el DE fuente que el Lookup querea — ese DE no tiene una fila para este SubscriberKey, o el filtro no matchea.
  • Ninguna fila para lookup-segment: el bloque AMPscript no llegó al lookup. Antes en el script hay un IF que ramificó mal, o una falla equivalente a Platform.Load, o un parse error.
  • Filas con valores no-NULL para cada paso: el AMPscript leyó los valores bien pero rendereó otra cosa. El bug está en la forma inline — probablemente %%@x%% para un local en vez de %%=v(@x)=%%.

Si el script no tiene logging en render time, este paso no devuelve nada. El próximo send necesita la instrumentación agregada; ver el Style Guide.

Paso 4 — Confusión de fuente: columna de DE vs Subscriber Attribute

Si los pasos 1-3 muestran que el data está y el script lo intentó leer, el bug es probablemente la confusión del gotcha #4AttributeValue("FirstName") leyó del Subscriber Attribute (perfil) mientras la columna en el DE sendable era donde el valor realmente vivía (o viceversa).

-- Comparar el valor del Subscriber Attribute vs el valor de la columna del DE sendable
-- para el destinatario afectado
SELECT
  a.SubscriberKey,
  a.FirstName AS DE_FirstName,
  s.FirstName AS Profile_FirstName
FROM de_send_audience_<send_name> a
LEFT JOIN _Subscribers s
  ON a.SubscriberKey = s.SubscriberKey
WHERE a.SubscriberKey = 'abc-12345';

Qué te dicen los dos valores:

  • DE_FirstName = "Mariana" y Profile_FirstName = NULL → el DE de audiencia tiene el data, pero el AMPscript leyó AttributeValue("FirstName") (que va al perfil, devolviendo NULL). Fix: cambialo a %%[FirstName]%% o la forma bracket para leer la columna del DE.
  • DE_FirstName = NULL y Profile_FirstName = "Mariana" → el perfil tiene el data, el DE no. El AMPscript leyó la columna del DE (que es NULL). Fix: o populá la columna del DE upstream, o cambiá el read a AttributeValue("FirstName").
  • Los dos populados, valores distintos: un problema de sync. Decidí qué fuente es canónica (según el Style Guide, usualmente es la columna del DE para contextos sendable), y alineá.

Misma forma que el gotcha original — la defensa es nombrar las fuentes distinto para que esta confusión sea imposible (DEFirstName vs ProfileFirstName).

Paso 5 — Recuperación + postmortem

Una vez encontrada la causa, capturala en de_log_ampscript_postmortems para referencia futura (misma convención que los snippets de debugging de SSJS).

INSERT INTO de_log_ampscript_postmortems
SELECT
  GETDATE()                                                                                          AS DiagnosedAt,
  'CP_PreferencesCenter'                                                                             AS AssetName,
  'JOB-2026-05-19-01'                                                                                AS JobID,
  'abc-12345'                                                                                        AS AffectedSubscriberKey,
  'FirstName en blanco en el saludo del header'                                                      AS Symptom,
  'la SQL de audience-build tenía un LEFT JOIN al DE legacy de perfil; ~3% de filas sin FirstName'   AS RootCause,
  'agregado INNER JOIN + default Empty en AMPscript; verificado el render del próximo send vía test' AS Fix;

Las columnas Symptom y RootCause fuerzan claridad. "Saludo en blanco" es el síntoma; "LEFT JOIN al DE legacy" es la causa raíz. Seis meses después cuando un blanco similar aparezca, la tabla de postmortem es la historia que te dice "ya vimos esta forma exacta — fue un problema de JOIN, chequeá la audience-build primero."

Causas comunes rankeadas por frecuencia

| Causa | Cómo detectarla | Fix en | |---|---|---| | Lookup/AttributeValue sin default de Empty() | El paso 3 muestra Value NULL; el paso 1 muestra que el DE fuente no tiene la fila | Style Guide; agregá IF Empty(@x) THEN ... ENDIF | | La SQL de audience-build deja la columna NULL para algunas filas | El PctMissing del paso 2 es más de algunos por ciento | Style Guide de SQL; auditá el JOIN que produce el DE de audiencia | | Confusión AttributeValue vs columna de DE | El paso 4 muestra el valor en una fuente pero el script leyó la otra | gotchas — #4; renombrá las fuentes distinto | | %%@x%% usado para una variable local que no resolvió | El paso 3 muestra que el valor se leyó bien pero el email igual rendereó en blanco | gotchas — #1; cambialo a %%=v(@x)=%% | | El destinatario no estaba en la audiencia (fan-out cross-Send) | El paso 1 no devuelve filas | Auditá la Automation; el email llegó al destinatario vía un Send distinto | | Lookup per-destinatario contra un DE sin la fila del destinatario | El paso 3 loggea el Value vacío pero el paso 1 confirma destinatario en audiencia | Pre-formateá upstream; el valor joineado va en el DE de audiencia | | Content block condicional escondido por un IF ramificando mal | El paso 3 no tiene fila de log para el paso esperado | Auditá la lógica condicional en el cuerpo del email |

Relacionado