El calendario de Microsoft 365 no llega al Mac: cómo despertar exchangesyncd
ContenidoContents
Me ha pasado más veces de las que me gusta admitir. Llega una invitación de trabajo, la acepto en el iPhone, el móvil la pinta en un segundo… y en el Mac mini, el que tengo delante todo el día, el hueco sigue vacío.
El correo de Microsoft 365 llega. iCloud sincroniza. Calendario no enseña ningún error. Cmd+R a veces no basta. Y al cabo de un rato —o de un día— te das cuenta de que el Mac no se ha enterado de la reunión.
No es tu cuenta. Es un clásico de Calendario en macOS con Exchange / Microsoft 365.
Qué está pasando
En el iPhone, Calendario habla con Exchange por ActiveSync y el empujón (push) funciona. En el Mac, Calendario también usa Exchange, pero el proceso que mantiene viva esa conversación se llama exchangesyncd. Hace un ping al servidor, y en macOS ese ping se muere en silencio. Sin aviso, sin triángulo amarillo, sin pedir la contraseña otra vez.
Mientras tanto el correo sigue llegando (otro canal) e iCloud sigue sincronizando (otro canal). Solo se queda dormido el calendario de trabajo.
En los foros de Apple el arreglo más repetido —desde Monterey hasta Sequoia y Tahoe— es forzar la salida de exchangesyncd. El proceso se relanza solo y el calendario vuelve a tirar… hasta que se vuelve a colgar. Hay hilos con más de cien votos. Hay gente con un cron cada hora haciendo exactamente eso. No es un invento mío.
Cómo saber si es esto (y no otra cosa)
Abre Calendario, barra lateral, y confirma que el calendario de Exchange está marcado. Si está, el siguiente paso es mirar cuándo sincronizó por última vez. En macOS reciente la base vive aquí:
~/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb
Lista las cuentas y su último sync (la fecha es Core Foundation: segundos desde el 1 de enero de 2001):
1python3 - <<'PY'
2import sqlite3, datetime, os
3db = os.path.expanduser(
4 "~/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb"
5)
6c = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
7APPLE = 978307200
8for name, start, end in c.execute(
9 "SELECT name, last_sync_start, last_sync_end FROM Store"
10):
11 def fmt(v):
12 return datetime.datetime.fromtimestamp(v + APPLE) if v else "—"
13 print(f"{name:30} {fmt(start)} → {fmt(end)}")
14PY
En mi caso iCloud había sincronizado esa misma mañana. El store de Exchange llevaba más de un día parado, y el último intento había durado 62 milisegundos: un latido vacío, no un volcado. Ahí está la pista. Si Exchange está en verde y aun así last_sync_end es de ayer, exchangesyncd se ha dormido.
El arreglo de treinta segundos
Cierra Calendario si quieres (no es obligatorio) y en Terminal:
1killall exchangesyncd
macOS lo vuelve a lanzar al momento. Abre Calendario y Vista → Actualizar calendarios (o Cmd+R). En unos segundos deberían aparecer las reuniones que el iPhone ya tenía.
Si no aparece nada, el siguiente escalón es desactivar y volver a activar Calendarios en Ajustes del Sistema → Cuentas de internet → la cuenta Exchange. Eso fuerza un resync completo. Es más brusco; yo empiezo por matar el proceso.
Dejarlo en piloto automático
Como el cuelgue vuelve, no sirve de mucho acordarse de killall cada vez que falta una reunión. En el Mini dejé un LaunchAgent que, cada 15 minutos, mira si el store de Exchange lleva más de 20 minutos sin sincronizar. Solo entonces relanza exchangesyncd.
Primero averigua el nombre exacto del store (el de la consulta anterior: en mi máquina es el de la cuenta de trabajo). Ponlo en STORE_NAME.
~/bin/refresh-o365-calendar.sh:
1#!/bin/zsh
2set -euo pipefail
3
4CALDB="$HOME/Library/Group Containers/group.com.apple.calendar/Calendar.sqlitedb"
5LOG="$HOME/Library/Logs/refresh-o365-calendar.log"
6STALE_SECS=1200
7STORE_NAME="Nombre de tu cuenta Exchange" # el de la consulta SQL
8
9ts() { date '+%Y-%m-%d %H:%M:%S'; }
10
11age=$(python3 - "$CALDB" "$STORE_NAME" <<'PY'
12import sqlite3, sys, time
13db, name = sys.argv[1], sys.argv[2]
14c = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
15row = c.execute("SELECT last_sync_end FROM Store WHERE name=?", (name,)).fetchone()
16if not row or row[0] is None:
17 sys.exit(2)
18print(int((time.time() - 978307200) - float(row[0])))
19PY
20) || {
21 echo "[$(ts)] no last_sync for $STORE_NAME, bouncing exchangesyncd" >>"$LOG"
22 killall exchangesyncd 2>/dev/null || true
23 exit 0
24}
25
26if (( age > STALE_SECS )); then
27 echo "[$(ts)] $STORE_NAME stale ${age}s, bouncing exchangesyncd" >>"$LOG"
28 killall exchangesyncd 2>/dev/null || true
29 if pgrep -x Calendar >/dev/null; then
30 osascript -e 'tell application "Calendar" to reload calendars' >/dev/null 2>&1 || true
31 fi
32fi
Hazlo ejecutable (chmod +x) y cárgalo con un plist en ~/Library/LaunchAgents/. Lo importante: StartInterval de 900 segundos y RunAtLoad.
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
3 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4<plist version="1.0">
5<dict>
6 <key>Label</key>
7 <string>com.example.refresh-o365-calendar</string>
8 <key>ProgramArguments</key>
9 <array>
10 <string>/bin/zsh</string>
11 <string>/Users/TU_USUARIO/bin/refresh-o365-calendar.sh</string>
12 </array>
13 <key>StartInterval</key>
14 <integer>900</integer>
15 <key>RunAtLoad</key>
16 <true/>
17</dict>
18</plist>
Cárgalo:
1launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/com.example.refresh-o365-calendar.plist
No relanza el proceso si acaba de sincronizar. Si Calendario está abierto, le pide un refresh para que la UI no se quede con el hueco vacío.
Lo que no es
No es que Microsoft 365 “no funcione en Mac”. Outlook para Mac, la web y el iPhone suelen estar al día. El que falla es Calendario.app hablando por Exchange. Tampoco es (casi nunca) un problema de contraseña: la cuenta sigue autenticada y el correo sigue entrando.
Si vives en Outlook, este artículo no te hace falta. Si quieres un solo Calendario nativo en el Mac, con iCloud, el de trabajo y el de los festivos, entonces sí: exchangesyncd es el que hay que vigilar.
Recapitulando
- Comprueba
last_sync_enddel store Exchange. - Si está parado:
killall exchangesyncdy Cmd+R. - Si te pasa a menudo: el LaunchAgent de arriba.
No es elegante. Es el mismo apaño que lleva años circulando en los foros de Apple, solo que aquí no hace falta acordarse.