Debugging de fallas silenciosas de Cloud-write
Un Cloud-write de AMPscript reportó success en render time. El email salió. Días después, el equipo de ventas nota que los registros de Salesforce que el email debía actualizar están desactualizados. UpdateSingleSalesforceObject devolvió 0 en silencio — y 0 significa siete cosas distintas. Cinco queries contra de_log_sf_writes que separan las siete.
El email salió. La Send Activity reportó Completed. El AMPscript que tenía que disparar UpdateSingleSalesforceObject("Lead", @leadId, "Status", "Engaged") para cada destinatario corrió, devolvió un número, y siguió. El cuerpo del email rendereó bien — el destinatario vio exactamente lo que tenía que ver. Días después, el equipo de ventas pregunta por qué los statuses de los leads en la lista target de la campaña no se movieron. La respuesta es que UpdateSingleSalesforceObject devolvió 0 (o peor, el AMPscript ni siquiera loggeó el valor de retorno), y un 0 puede significar siete cosas distintas sin mensaje distintivo — ver gotchas — #10 y funciones Cloud-write.
Esta página es el playbook de diagnóstico para esa forma exacta. Cinco queries contra de_log_sf_writes (la tabla de log que la disciplina del Style Guide manda) que separan "no disparó" de "disparó pero bloqueado", y el procedimiento de recuperación que re-corre los writes contra los subscribers afectados.
El lifecycle de Cloud-write y dónde falla
[ AMPscript renderea email para destinatario ]
↓ el gate de messagecontext evalúa
[ Gate true (Send) → dispara UpdateSingleSalesforceObject ]
↓ llamada síncrona a través de MC Connect
[ Salesforce procesa el intento de write ]
↓ muchas formas de falla devuelven 0 en silencio:
↓ • Registro no encontrado (Id equivocado o borrado desde audience-build)
↓ • El API user no tiene permiso sobre el objeto
↓ • Field-level security bloquea el campo
↓ • Validation rule rechaza
↓ • Registro lockeado (aprobación, sharing)
↓ • Sesión de MC Connect expiró a mitad del send
↓ • El valor del campo no matchea el tipo (valor de picklist malo)
[ La función devuelve 1 o 0; AMPscript continúa ]
↓ Style Guide: loggear a de_log_sf_writes
[ El diagnóstico pasa acá — días después ]Cada modo de falla en la flecha "muchas formas de falla" se ve idéntico desde el punto de vista de AMPscript: 0. Las queries de diagnóstico abajo las separan leyendo la tabla de log que el script dejó atrás más cross-referenciando con el data de auditoría propio de Salesforce.
Paso 1 — ¿Hay un log para nada?
La regla del Style Guide de Cleon es: cada UpdateSingleSalesforceObject y CreateSalesforceObject está seguido de un InsertData a de_log_sf_writes registrando el JobID, SubscriberKey, tipo de objeto SF, Id del registro SF, operación, valor de retorno, y timestamp.
Si el AMPscript siguió la regla, el log existe. Si no, el diagnóstico es mucho más difícil — arrancá leyendo el script para confirmar si el logging estaba en su lugar.
-- Confirmar que existen filas para el JobID afectado
SELECT
COUNT(*) AS TotalAttempts,
SUM(CASE WHEN Result = 1 THEN 1 ELSE 0 END) AS Successes,
SUM(CASE WHEN Result = 0 THEN 1 ELSE 0 END) AS Failures
FROM de_log_sf_writes
WHERE JobID = 'JOB-2026-05-19-01';Tres formas de falla:
TotalAttempts = 0: sin filas de log. O el AMPscript no corrió el Cloud-write directamente (el gate evaluó false, rama oculta, typo en JobID), o el logging directamente no estaba implementado. Chequeá el código AMPscript; si el logging no está, el resto de esta página no te ayuda — agregá la disciplina de logging y el próximo send queda diagnosticable.TotalAttempts > 0yFailures = 0: sin fallas registradas — cada write devolvió1. Pero ventas reporta que los writes no aterrizaron. Dos posibilidades: los writes devolvieron1a AMPscript pero Salesforce los rechazó downstream (raro pero pasa con triggers async), o el script loggeó los subscribers equivocados / JobID equivocado. Cross-referenciá con el DE de audiencia.TotalAttempts > 0yFailures > 0: los writes intentaron pero una porción falló. Seguí al paso 2 para bucketear las fallas.
Paso 2 — ¿Qué operaciones fallan más que otras?
Si las fallas están concentradas en una operación (digamos, Lead.Status update falla 80% del tiempo mientras Task.Create tiene éxito al 100%), el modo de falla es específico a esa operación — usualmente FLS o una validation rule sobre el campo afectado.
SELECT
Operation,
COUNT(*) AS Attempts,
SUM(CASE WHEN Result = 1 THEN 1 ELSE 0 END) AS Successes,
SUM(CASE WHEN Result = 0 THEN 1 ELSE 0 END) AS Failures,
CAST(SUM(CASE WHEN Result = 0 THEN 1 ELSE 0 END) * 100.0 / COUNT(*) AS DECIMAL(5,2)) AS FailurePct
FROM de_log_sf_writes
WHERE JobID = 'JOB-2026-05-19-01'
GROUP BY Operation
ORDER BY FailurePct DESC;Qué te dice el breakdown:
- Una operación con casi 100% de falla: la operación está rota del lado de SF. Probablemente una validation rule, FLS, o error de campo requerido. Abrí el debug log de SF para cualquiera de esos registros (usando el SfId de la tabla de log) y chequeá qué regla se disparó.
- Una operación con tasa de falla moderada (digamos 10-30%): la falla es data-dependent. Algunos registros no tienen el prerequisito que el write necesita, o algunos registros están en un estado (lockeado, en aprobación) que bloquea el write.
- Todas las operaciones fallan más o menos parejo: no es específica de la operación. La sesión de MC Connect probablemente expiró, o el API user perdió un permiso crítico, o hay un hit tenant-wide de rate-limit.
Paso 3 — Distribución de fallas a lo largo del tiempo
El patrón de fallas a lo largo del tiempo te dice si la sesión de MC Connect se cayó a mitad del send.
SELECT
DATEPART(hour, Ts) AS Hour,
DATEPART(minute, Ts) AS Minute,
COUNT(*) AS Attempts,
SUM(CASE WHEN Result = 0 THEN 1 ELSE 0 END) AS Failures
FROM de_log_sf_writes
WHERE JobID = 'JOB-2026-05-19-01'
GROUP BY DATEPART(hour, Ts), DATEPART(minute, Ts)
ORDER BY Hour, Minute;Tres patrones:
- Fallas distribuidas parejo a lo largo de toda la ventana del send: la causa de la falla es per-record, no a nivel de sesión. Andá al paso 4 para bucketear por subscriber.
- Fallas agrupadas en un minuto específico, después del cual la tasa se dispara a casi 100%: el token de sesión de MC Connect expiró o fue revocado en ese minuto. Cada write después de ese punto falló. El fix está del lado de MC Connect (re-autenticar la conexión, auditar la política de rotación de token); la recuperación inmediata es re-correr los writes contra los subscribers fallados vía SSJS Activity batch (ver funciones Cloud-write — "Mover a SSJS Script Activity en su lugar").
- Fallas agrupadas en ráfagas: throttling de rate-limit. La API de Salesforce rechazó llamadas por chunks. Para sends de alto volumen, esto significa que los Cloud-writes de AMPscript fueron la arquitectura equivocada — mover a SSJS bulk o SQL-Activity-contra-DEs-sincronizados.
Paso 4 — Cross-referenciá las fallas con el estado del registro SF
Para una muestra de writes fallados, traé el estado actual del registro SF para ver si el cambio pretendido falta.
-- El log registra el SfId; usalo para querear el DE SF sincronizado.
-- (Reemplazá 'Lead_Salesforce' con el nombre de tu DE sincronizado;
-- el nombre depende de la configuración de MC Connect.)
SELECT
l.SubscriberKey,
l.SfId,
l.Operation,
l.Result AS LogResult,
l.Ts AS LogTs,
lead.Status AS CurrentStatus,
lead.LastModifiedDate
FROM de_log_sf_writes l
LEFT JOIN Lead_Salesforce lead
ON l.SfId = lead.Id
WHERE l.JobID = 'JOB-2026-05-19-01'
AND l.Result = 0
AND l.Operation = 'Lead.Status=Engaged'
ORDER BY l.Ts;Tres confirmaciones:
CurrentStatusNO esEngaged: el write efectivamente no aterrizó. El log dice0y SF confirma sin cambio.CurrentStatusesEngaged: el write aterrizó a pesar del retorno0. Raro pero posible con procesamiento async de SF — la función reportó falla antes de que el backend de SF terminara de commitear. Investigá el debug log de SF para el registro afectado para ver si el write tuvo éxito con un warning que la función no manifestó.LastModifiedDatees después deLogTs: alguien o algo más actualizó el registro después del intento del AMPscript. El estado actual no está relacionado con lo que el AMPscript intentó; necesitás el historial de auditoría de SF para saber.
Paso 5 — Recuperación: re-correr contra los subscribers fallados
Una vez entendido el patrón de falla, recuperate re-emitiendo los writes contra la lista de subscribers-fallados. No re-mandes el email; solo re-emití los writes de SF vía una SSJS Script Activity que batchea más confiablemente que el AMPscript inline.
-- Armar la lista de recuperación: subscribers cuyo Cloud-write falló
SELECT
l.SubscriberKey,
l.SfId,
l.Operation
INTO de_recovery_sf_writes_<jobid>
FROM de_log_sf_writes l
LEFT JOIN Lead_Salesforce lead
ON l.SfId = lead.Id
AND lead.Status = 'Engaged' /* excluir filas donde el write efectivamente aterrizó */
WHERE l.JobID = 'JOB-2026-05-19-01'
AND l.Result = 0
AND lead.Id IS NULL;Entregá el DE de recuperación a una SSJS Script Activity (usando Platform.Function.UpdateData contra DEs sincronizados, o WSProxy para updates directos a SF) que batchea los reintentos con logging apropiado — ver MC SSJS — debugging WSProxy auth para el patrón de auth-refresh necesario en cualquier loop largo de reintento.
Después de la recuperación, escribí el postmortem a de_log_ampscript_postmortems (mismo patrón que los otros snippets de debugging de AMPscript):
INSERT INTO de_log_ampscript_postmortems
SELECT
GETDATE() AS DiagnosedAt,
'WelcomeEmail_LeadStatusUpdate' AS AssetName,
'JOB-2026-05-19-01' AS JobID,
NULL AS AffectedSubscriberKey,
'14% de las llamadas a UpdateSingleSalesforceObject(Lead.Status) devolvieron 0; ventas vio data stale' AS Symptom,
'FLS sobre Status cambió para el perfil del API user dos días antes del send; los writes fallaron silenciosamente' AS RootCause,
'FLS restaurado; re-corrido vía SSJS batch desde de_recovery_sf_writes_*; runbook de rotación de permisos actualizado' AS Fix;La columna RootCause fuerza claridad. "FLS cambió hace dos días" es la causa raíz; el síntoma fue data stale. El Fix incluye tanto la recuperación inmediata como el update del runbook para que la rotación que causó esto se maneje distinto la próxima vez.
Causas comunes rankeadas por frecuencia
| Causa | Cómo detectarla | Fix en |
|---|---|---|
| Cambio de FLS o permiso en el API user | El paso 2 muestra una operación con casi 100% de falla | Auditar el perfil del API user de MC Connect en Salesforce Setup |
| Sesión de MC Connect expiró a mitad del send | El paso 3 muestra corte limpio después del cual la tasa de falla se dispara a casi 100% | Re-autenticar MC Connect; auditar política de rotación de token |
| Validation rule sobre el objeto SF | El paso 2 muestra una operación con tasa de falla moderada; el SF debug log revela la regla | Coordinar con el equipo de admin de SF; ajustar la regla o pre-validar upstream |
| Throttling de rate-limit en la API de SF | El paso 3 muestra ráfagas agrupadas de fallas | Mover de AMPscript inline a SSJS batch (ver referencia Cloud-write) |
| SfId equivocado en el DE de audiencia | El paso 4 muestra LEFT JOIN devolviendo NULL para muchos writes fallados | Auditar la SQL de audience-build; asegurar que SfId viene de la fuente correcta |
| Campo requerido en Create faltante | El paso 2 muestra CreateSalesforceObject fallando | Auditar el conteo de pares de campos y la lista de required-fields contra el schema de SF |
| Logging de AMPscript faltante | El paso 1 no devuelve filas pero hubo intentos de write | Agregar el patrón de fila de log InsertData a cada call site de Cloud-write |
| Cloud-write fuera del gate _messagecontext | Aparecen filas de log de QA previews | Auditar gate del Style Guide; nunca escribir fuera del perímetro |
Relacionado
- Basics — los contextos de runtime en los que corre AMPscript
- Funciones Cloud-write — el inventario de funciones y los patrones que esta página diagnostica
- Funciones de Data Extension —
InsertDatapara el patrón de fila de log - Funciones de Subscriber + Profile —
_messagecontextpara el gate perímetro que cada Cloud-write necesita - Gotchas de MC AMPscript — ver #10 (patrón de falla silenciosa de Cloud-write, el gotcha que esta página diagnostica)
- Style Guide — la disciplina merge-time que manda
de_log_sf_writespara cada Cloud-write - Debugging de blancos en render time — snippet hermano de debugging (fallas dentro del cuerpo del email)
- Debugging de mismatch preview-vs-send — snippet hermano de debugging (fallas de divergencia de contexto)
- MC SSJS — Debugging de auth de WSProxy — el patrón SSJS para loops largos de reintento necesario al recuperar de fallas de Cloud-write
Progreso del catálogo con esta página: la sección AMPscript cierra a 14 page-pairs — 1 production-note + 9 reference + 1 decision-framework + 3 how-to debugging. Misma forma que los catálogos de SQL y SSJS. Próximo: subcategoría Config (actualmente vacía).