Resumen
La noche del 17-05-2026 (sesión s68) se detectó que al cerrar una tarea en CreaRack, el evento correspondiente seguía visible en Zoho Calendar aunque la fila en D1 había sido borrada correctamente. La causa raíz fue la ausencia del header etag en la petición DELETE al API de Zoho Calendar.
Síntoma
- Tarea cerrada →
onTaskUpdateddisparadeleteEventsByType→ borra fila enzoho_calendar_links(D1) ✅ - Petición DELETE a Zoho API → respuesta
400 ETAG_MISSING→ evento permanece en Zoho ❌ - El
catchgenérico silenciaba el error; la discrepancia pasaba desapercibida hasta inspección visual del calendario.
Respuesta exacta de Zoho:
{"error_code": "ETAG_MISSING", "message": "ETAG_MISSING"}
Causa raíz
La Zoho Calendar API implementa concurrencia optimista vía ETag: cada recurso tiene una versión (etag) que debe enviarse en el header etag de todas las operaciones de escritura (DELETE, PUT). Sin él, el servidor rechaza la petición con HTTP 400.
El código anterior en deleteEventsByType y en el bloque de onTaskDeleted omitía este header:
// ANTES (bug)
await zohoFetch(env, member, url, { method: 'DELETE' });
Fix aplicado (commit e779e6a)
Se introdujo la función fetchEventEtag que realiza un GET previo al evento para obtener el ETag actual, y luego lo incluye en el DELETE:
// DESPUÉS (fix)
const etag = await fetchEventEtag(env, member, row.calendar_uid, row.zoho_event_uid);
if (!etag) {
console.warn(`[task-zoho-auto] DELETE ${row.zoho_event_uid}: no etag (ya borrado en Zoho)`);
continue; // skip — evento ya no existe
}
await zohoFetch(env, member, url, { method: 'DELETE', headers: { etag } });
El patrón también cubre el caso de evento ya borrado en Zoho: si el GET devuelve 404 (!res.ok), fetchEventEtag retorna null y el DELETE se omite, evitando errores espurios.
Impacto
| Dimensión | Detalle |
|---|---|
| Severidad | Media — datos inconsistentes entre D1 y Zoho Calendar (no bloqueo de prod) |
| Ventana | Indeterminada (bug presente antes de s68, detectado esa noche) |
| Afectados | Todos los usuarios con integración Zoho Calendar activa al cerrar tareas |
| Pérdida de datos | No — los eventos sólo quedaban “huérfanos” en Zoho; ningún dato interno perdido |
Acciones tomadas
- Diagnóstico vía endpoint de debug que retornó el body
ETAG_MISSING. - Fix en
functions/_lib/task-zoho-auto.ts— patrón GET → ETag → DELETE. - Commit directo a
maine779e6apor @Esquembri, co-autorado con Claude Opus 4.7. - Limpieza manual de eventos huérfanos en Zoho (si necesaria, fuera de scope del commit).
Lecciones aprendidas
- La Zoho Calendar API requiere ETag en todas las operaciones de escritura. Documentar este patrón para futuros endpoints (
PUTde actualización de evento también lo necesitará). - El
catchgenérico endeleteEventsByTypeocultaba el400de Zoho. El nuevo código añadeconsole.warnexplícito con status + body para detectar fallos silenciosos. - Considerar un test de integración que valide que el DELETE incluye el header
etag.
Véase también
- [[entity—zoho—service—task-zoho-auto]]
- [[entity—zoho—schema—zohoevent]]
- [[entity—zoho—schema—zohoenv]]
- [[entity—zoho—schema—zohotaskuser]]
- [[entity—zoho—schema—zohogroup]]