Si usas OpenClaw para tener un agente de IA accesible por WhatsApp, sabes que la experiencia es increíble... hasta que deja de funcionar. Un reinicio del gateway, un token expirado, un DNS que falla — y de repente tu agente está muerto y nadie te avisa.

If you use OpenClaw to have an AI agent accessible via WhatsApp, you know the experience is incredible... until it stops working. A gateway restart, an expired token, a failed DNS — and suddenly your agent is dead and nobody tells you.

En este post te explico cómo configuré mi setup para que sea prácticamente indestructible: reconexión automática de WhatsApp, un watchdog que revisa cada 15 minutos, y 4 proveedores de modelo en cadena de failover para que si uno se cae, el siguiente entre inmediatamente.

In this post I explain how I configured my setup to be practically indestructible: automatic WhatsApp reconnection, a watchdog that checks every 15 minutes, and 4 model providers in a failover chain so if one goes down, the next one steps in immediately.

🦞 ¿Qué es OpenClaw?🦞 What is OpenClaw?

OpenClaw es una plataforma open-source para crear agentes de IA con acceso a canales de mensajería (WhatsApp, Telegram, etc.), herramientas del sistema, y múltiples proveedores de modelos. Básicamente, un agente que vive en tu terminal y responde por WhatsApp.

OpenClaw is an open-source platform for creating AI agents with access to messaging channels (WhatsApp, Telegram, etc.), system tools, and multiple model providers. Basically, an agent that lives in your terminal and responds via WhatsApp.

1. El Problema1. The Problem

Todo empezó cuando noté que mi agente de OpenClaw había dejado de responder por WhatsApp. Le mandaba mensajes y nada. Silencio absoluto.

It all started when I noticed my OpenClaw agent had stopped responding via WhatsApp. I'd send messages and nothing. Complete silence.

Al revisar los logs, encontré dos errores que se estaban encadenando:

Looking at the logs, I found two errors that were chaining together:

⚠️ Error 1: DNS temporal — ENOTFOUND⚠️ Error 1: Temporary DNS — ENOTFOUND

El gateway de WhatsApp no podía resolver web.whatsapp.com. Esto pasa cuando hay una interrupción de red momentánea o el gateway se reinicia y la resolución DNS aún no está lista.

The WhatsApp gateway couldn't resolve web.whatsapp.com. This happens when there's a momentary network interruption or the gateway restarts and DNS resolution isn't ready yet.

[ERROR] WhatsApp channel disconnected
  reason: getaddrinfo ENOTFOUND web.whatsapp.com
  at: 2026-02-13T08:42:17.003Z
  retry_count: 3
  status: STOPPED

⚠️ Error 2: Token de Copilot expirado — HTTP 403⚠️ Error 2: Copilot Token Expired — HTTP 403

El proveedor principal (GitHub Copilot) estaba devolviendo 403 Forbidden. El token de autenticación había expirado y no se renovó automáticamente. Resultado: aún cuando el canal se reconectara, el agente no podía generar respuestas.

The primary provider (GitHub Copilot) was returning 403 Forbidden. The authentication token had expired and didn't auto-renew. Result: even when the channel reconnected, the agent couldn't generate responses.

[ERROR] Model provider failed: github-copilot/claude-opus-4.6
  status: 403 Forbidden
  message: "Token expired or revoked"
  fallback: none configured

O sea: doble falla. El canal de comunicación muerto Y el proveedor de modelo muerto. Sin fallback. Sin watchdog. Sin recuperación automática. Un desastre.

In other words: double failure. The communication channel dead AND the model provider dead. No fallback. No watchdog. No automatic recovery. A disaster.

2. Diagnóstico2. Diagnosis

Antes de arreglar nada, hay que entender qué pasó. OpenClaw tiene comandos muy útiles para diagnosticar el estado de todo el sistema.

Before fixing anything, you need to understand what happened. OpenClaw has very useful commands for diagnosing the state of the entire system.

Revisar el estado de los canalesCheck Channel Status

openclaw channels status

Esto muestra el estado de cada canal conectado. En mi caso, WhatsApp aparecía como STOPPED:

This shows the status of each connected channel. In my case, WhatsApp appeared as STOPPED:

Channel      Status      Last Activity         Uptime
─────────    ────────    ──────────────────    ──────
whatsapp     STOPPED     2026-02-13 08:42      0s
terminal     CONNECTED   2026-02-14 10:15      1d 2h

Revisar los logs del canalCheck Channel Logs

openclaw channels logs --channel whatsapp --tail 50

Aquí es donde encontré los errores de DNS que mencioné arriba. Los logs te dan el detalle completo de qué pasó y cuándo.

This is where I found the DNS errors I mentioned above. The logs give you the full detail of what happened and when.

Health check generalGeneral Health Check

openclaw health
Component          Status    Details
─────────────      ──────    ───────
Gateway            ✓ OK     pid: 48291, uptime: 1d 2h
WhatsApp Channel   ✗ FAIL   STOPPED - ENOTFOUND web.whatsapp.com
Model: openai      ✓ OK     gpt-5.3-codex responding
Model: copilot     ✗ FAIL   403 Forbidden - token expired
Node.js            ✓ OK     v22.14.0
Cron               ✓ OK     0 active jobs

💡 Pro tip

Siempre corre openclaw health primero. Te da una vista panorámica de todo lo que está bien y mal en un solo comando. Desde ahí sabes dónde enfocarte.

Always run openclaw health first. It gives you a panoramic view of everything that's working and not in a single command. From there you know where to focus.

3. Solución 1: Reconectar WhatsApp3. Solution 1: Reconnect WhatsApp

Lo primero es lo primero: restaurar la conexión del canal de WhatsApp.

First things first: restore the WhatsApp channel connection.

Paso 1: Re-autenticar el canalStep 1: Re-authenticate the Channel

openclaw channels login --channel whatsapp

Esto regenera la sesión de WhatsApp. Te va a pedir escanear un código QR desde tu teléfono (igual que WhatsApp Web). Una vez escaneado, la sesión queda activa.

This regenerates the WhatsApp session. It will ask you to scan a QR code from your phone (just like WhatsApp Web). Once scanned, the session becomes active.

Paso 2: Reiniciar el gatewayStep 2: Restart the Gateway

openclaw gateway restart

El gateway es el proceso que mantiene las conexiones activas. Al reiniciarlo, se reconectan todos los canales con las sesiones actualizadas.

The gateway is the process that keeps connections active. When restarting it, all channels reconnect with updated sessions.

Gateway restarting...
  WhatsApp: reconnecting... ✓ CONNECTED
  Terminal: reconnecting... ✓ CONNECTED
Gateway ready (pid: 51023)

Paso 3: VerificarStep 3: Verify

openclaw channels status

Channel      Status       Last Activity         Uptime
─────────    ─────────    ──────────────────    ──────
whatsapp     CONNECTED    2026-02-14 10:30      2m
terminal     CONNECTED    2026-02-14 10:30      1d 2h

✔ Canal restaurado✔ Channel Restored

WhatsApp vuelve a estar CONNECTED. Pero esto fue un fix manual. Si se vuelve a caer a las 3am, ¿quién lo reconecta? Nadie. Por eso necesitamos el watchdog.

WhatsApp is CONNECTED again. But this was a manual fix. If it goes down again at 3am, who reconnects it? Nobody. That's why we need the watchdog.

4. Solución 2: Watchdog Automático (Cron)4. Solution 2: Automatic Watchdog (Cron)

La idea es simple: un cron job que corre cada 15 minutos, revisa si WhatsApp está conectado, y si no lo está, lo reconecta automáticamente. Lo mejor es que OpenClaw tiene esto integrado — no necesitas crontab del sistema ni scripts externos.

The idea is simple: a cron job that runs every 15 minutes, checks if WhatsApp is connected, and if it isn't, reconnects it automatically. The best part is that OpenClaw has this built in — you don't need system crontab or external scripts.

Crear el watchdogCreate the Watchdog

openclaw cron add \
  --name "whatsapp-watchdog" \
  --every 15m \
  --message "Revisa el estado del canal de WhatsApp. Si está desconectado o stopped, ejecuta 'openclaw gateway restart' para reconectarlo." \
  --channel whatsapp \
  --to "+52XXXXXXXXXX" \
  --best-effort-deliver

¿Qué hace esto exactamente?

What does this do exactly?

🔄 ¿Cómo funciona internamente?🔄 How Does It Work Internally?

El cron de OpenClaw no es un crontab de Unix. Es un scheduler interno que vive en el gateway. Cada 15 minutos, invoca al agente con el mensaje que le diste, el agente analiza el estado usando sus herramientas (channels_status, gateway_restart, etc.) y toma las acciones necesarias. Es como tener un DevOps bot viviendo en tu máquina.

The OpenClaw cron is not a Unix crontab. It's an internal scheduler that lives in the gateway. Every 15 minutes, it invokes the agent with the message you gave it, the agent analyzes status using its tools (channels_status, gateway_restart, etc.) and takes the necessary actions. It's like having a DevOps bot living on your machine.

Verificar que el cron está activoVerify That the Cron Is Active

openclaw cron list

Name                  Interval    Next Run              Channel     Status
──────────────────    ────────    ──────────────────    ─────────   ──────
whatsapp-watchdog     15m         2026-02-14 10:45      whatsapp    ACTIVE

✔ Watchdog activo✔ Watchdog Active

Ahora si WhatsApp se desconecta a las 3am, el watchdog lo detecta en máximo 15 minutos y lo reconecta solo. Puedes dormir tranquilo.

Now if WhatsApp disconnects at 3am, the watchdog detects it in at most 15 minutes and reconnects it on its own. You can sleep easy.

5. Solución 3: Multi-Provider Failover5. Solution 3: Multi-Provider Failover

El canal ya se reconecta solo. Pero ¿qué pasa si el modelo falla? Si tu único proveedor es GitHub Copilot y el token expira, el agente recibe mensajes pero no puede responder. Necesitamos un plan B. Y un plan C. Y un plan D.

The channel already reconnects on its own. But what if the model fails? If your only provider is GitHub Copilot and the token expires, the agent receives messages but can't respond. We need a plan B. And a plan C. And a plan D.

La cadena de failoverThe Failover Chain

PrioridadPriority ProveedorProvider ModeloModel NotasNotes
Primary OpenAI Codex openai-codex/gpt-5.3-codex El más rápido, ideal para respuestas del día a díaThe fastest, ideal for day-to-day responses
Fallback #1 GitHub Copilot github-copilot/claude-opus-4.6 Claude Opus vía Copilot proxy, muy capazClaude Opus via Copilot proxy, very capable
Fallback #2 Copilot Proxy copilot-proxy/claude-sonnet-4.5 Sonnet vía Copilot, más ligero pero confiableSonnet via Copilot, lighter but reliable
Fallback #3 Anthropic (Claude Code) anthropic/claude-sonnet-4-5-20250929 Directo desde Claude Code subscriptionDirectly from Claude Code subscription

La lógica es: si el proveedor primario falla (timeout, 403, 500, rate limit), OpenClaw automáticamente intenta el siguiente en la lista. Si ese también falla, pasa al siguiente. Así sucesivamente hasta que uno responda.

The logic is: if the primary provider fails (timeout, 403, 500, rate limit), OpenClaw automatically tries the next one on the list. If that one also fails, it moves to the next. And so on until one responds.

Configurar los fallbacksConfigure the Fallbacks

openclaw config set agents.defaults.model.fallbacks \
  '["github-copilot/claude-opus-4.6","copilot-proxy/claude-sonnet-4.5","anthropic/claude-sonnet-4-5-20250929"]' \
  --json
✓ Config updated: agents.defaults.model.fallbacks
   [0] github-copilot/claude-opus-4.6
   [1] copilot-proxy/claude-sonnet-4.5
   [2] anthropic/claude-sonnet-4-5-20250929

💡 ¿Por qué este orden?💡 Why This Order?

El modelo primario (openai-codex/gpt-5.3-codex) ya está configurado como default. Los fallbacks se prueban en orden. Puse Claude Opus primero porque es el más capaz. Sonnet como segundo porque es rápido y barato. Y el de Anthropic directo como último recurso porque usa los créditos de mi suscripción de Claude Code.

The primary model (openai-codex/gpt-5.3-codex) is already configured as default. Fallbacks are tried in order. I put Claude Opus first because it's the most capable. Sonnet as second because it's fast and cheap. And Anthropic direct as last resort because it uses my Claude Code subscription credits.

6. Configurar GitHub Copilot como Provider6. Configure GitHub Copilot as Provider

Para que OpenClaw pueda usar modelos vía GitHub Copilot, necesitas autenticarte con el device flow de GitHub:

For OpenClaw to use models via GitHub Copilot, you need to authenticate with the GitHub device flow:

openclaw models auth login-github-copilot
🔐 GitHub Copilot Authentication
   Open this URL in your browser:
   → https://github.com/login/device

   Enter this code: ABCD-1234

   Waiting for authorization...

El proceso es:

The process is:

  1. Abre https://github.com/login/device en tu navegador.
  2. Open https://github.com/login/device in your browser.
  3. Ingresa el código que te muestra la terminal.
  4. Enter the code shown in the terminal.
  5. Autoriza el acceso.
  6. Authorize access.
  7. La terminal detecta la autorización automáticamente.
  8. The terminal automatically detects the authorization.
   ✓ Authenticated as @oscarcode9
   Token stored securely.
   Available models: claude-opus-4.6, claude-sonnet-4.5, gpt-4o, o1...

⚠️ Token expiration

Los tokens de Copilot expiran periódicamente. Si ves errores 403, re-ejecuta openclaw models auth login-github-copilot para renovar. Con el failover configurado, incluso si el token expira, los otros proveedores toman el relevo mientras lo renuevas.

Copilot tokens expire periodically. If you see 403 errors, re-run openclaw models auth login-github-copilot to renew. With failover configured, even if the token expires, the other providers take over while you renew it.

7. Configurar Claude Code como Provider7. Configure Claude Code as Provider

Esta es la parte más elegante del setup. OpenClaw puede leer las credenciales de Claude Code directamente desde el macOS Keychain. Si ya tienes Claude Code instalado y autenticado, no necesitas configurar tokens ni API keys manualmente.

This is the most elegant part of the setup. OpenClaw can read Claude Code credentials directly from the macOS Keychain. If you already have Claude Code installed and authenticated, you don't need to configure tokens or API keys manually.

🔑 Credenciales automáticas🔑 Automatic Credentials

OpenClaw busca las credenciales en el Keychain de macOS bajo el nombre Claude Code-credentials. Si Claude Code está autenticado en tu máquina, OpenClaw las encuentra y las usa directamente. Zero config.

OpenClaw looks for credentials in the macOS Keychain under the name Claude Code-credentials. If Claude Code is authenticated on your machine, OpenClaw finds and uses them directly. Zero config.

Lo único que necesitas hacer es decirle a OpenClaw que ese modelo existe como opción:

The only thing you need to do is tell OpenClaw that model exists as an option:

openclaw config set agents.defaults.models.anthropic/claude-sonnet-4-5-20250929 '{}' --json
✓ Config updated: agents.defaults.models.anthropic/claude-sonnet-4-5-20250929
   Auth: macOS Keychain (Claude Code-credentials) ✓
   Model: claude-sonnet-4-5-20250929
   Status: ready

Eso es todo. El {} vacío significa "usa las credenciales que ya tienes". OpenClaw detecta el Keychain, extrae el token, y lo usa cuando necesita hacer requests a la API de Anthropic.

That's it. The empty {} means "use the credentials you already have". OpenClaw detects the Keychain, extracts the token, and uses it when it needs to make requests to the Anthropic API.

💡 ¿Por qué es el último fallback?💡 Why Is It the Last Fallback?

Porque los requests a la API directa de Anthropic se descuentan de tu suscripción de Claude Code. Los otros proveedores (Copilot) usan sus propios créditos. Entonces reservo Anthropic directo como último recurso para no gastar créditos innecesariamente.

Because requests to the direct Anthropic API are deducted from your Claude Code subscription. The other providers (Copilot) use their own credits. So I reserve Anthropic direct as a last resort to avoid spending credits unnecessarily.

8. Instalar Node.js del Sistema8. Install System Node.js

El gateway de OpenClaw corre sobre Node.js. Si estás usando la versión que viene con algún version manager (nvm, fnm, etc.), puede haber problemas de estabilidad cuando el gateway se ejecuta como proceso de fondo — especialmente después de reinicios del sistema.

The OpenClaw gateway runs on Node.js. If you're using the version that comes with a version manager (nvm, fnm, etc.), there can be stability issues when the gateway runs as a background process — especially after system restarts.

La solución es instalar Node.js directamente con Homebrew para que esté disponible a nivel de sistema:

The solution is to install Node.js directly with Homebrew so it's available system-wide:

brew install node

Después, ejecuta el doctor de OpenClaw para que detecte y use la instalación del sistema:

Then, run the OpenClaw doctor so it detects and uses the system installation:

openclaw doctor --fix
🔍 Running diagnostics...

  Node.js        ✓  v22.14.0 (/opt/homebrew/bin/node)
  npm            ✓  v10.9.2
  Gateway        ✓  running (pid: 51023)
  WhatsApp       ✓  CONNECTED
  Models         ✓  4 configured (1 primary + 3 fallbacks)
  Cron           ✓  1 active job
  Keychain       ✓  Claude Code-credentials found

✓ All checks passed. No fixes needed.

✔ Sistema estable✔ Stable System

Con Node.js instalado vía Homebrew, el gateway tiene una instalación de Node confiable que no depende de la sesión de shell ni de variables de entorno de nvm. Esto es especialmente importante para que el watchdog funcione correctamente en background.

With Node.js installed via Homebrew, the gateway has a reliable Node installation that doesn't depend on the shell session or nvm environment variables. This is especially important for the watchdog to work correctly in the background.

9. Verificar Todo9. Verify Everything

Ya tenemos todo configurado. Ahora hay que probar que cada proveedor funciona y que el canal de WhatsApp entrega los mensajes correctamente.

Everything is configured. Now let's verify that each provider works and that the WhatsApp channel delivers messages correctly.

Probar el envío por WhatsAppTest WhatsApp Delivery

openclaw agent \
  -m "Responde con: Soy [provider] y estoy funcionando" \
  --to "+52XXXXXXXXXX" \
  --channel whatsapp \
  --deliver

Si todo está bien, deberías recibir un mensaje en WhatsApp con el nombre del proveedor que respondió.

If everything is working, you should receive a message on WhatsApp with the name of the provider that responded.

Verificar la configuración de modelosVerify Model Configuration

openclaw models list
Role          Provider                Model                              Status
──────────    ──────────────────      ─────────────────────────────      ──────
default       openai-codex            gpt-5.3-codex                      ✓ OK
fallback#1    github-copilot          claude-opus-4.6                    ✓ OK
fallback#2    copilot-proxy           claude-sonnet-4.5                  ✓ OK
fallback#3    anthropic               claude-sonnet-4-5-20250929         ✓ OK

Los 4 proveedores deben mostrar OK. Si alguno muestra error, revisa la autenticación de ese proveedor específico.

All 4 providers should show OK. If any shows an error, check the authentication for that specific provider.

Probar el failover manualmenteTest Failover Manually

# Deshabilitar temporalmente el proveedor primario
openclaw config set agents.defaults.model.enabled false

# Enviar un mensaje — debería usar fallback#1
openclaw agent -m "¿Qué modelo me está respondiendo?" --to "+52XXXXXXXXXX" --channel whatsapp --deliver

# Re-habilitar el proveedor primario
openclaw config set agents.defaults.model.enabled true

✔ Failover verificado✔ Failover Verified

Si recibes respuesta cuando el primario está deshabilitado, el failover está funcionando correctamente. El agente cambió automáticamente al siguiente proveedor disponible.

If you receive a response when the primary is disabled, failover is working correctly. The agent automatically switched to the next available provider.

10. Resultado Final10. Final Result

Aquí está el diagrama completo del flujo de failover:

Here's the complete failover flow diagram:

📱 WhatsApp Message │ ↓ ┌────────────────────────┐ │ OpenClaw Gateway │◄──── Watchdog (cada 15min / every 15min) │ (Node.js process) │ reconecta si STOPPED / reconnects if STOPPED └────────┬───────────────┘ │ ↓ ┌────────────────────────┐ │ Model Router │ │ (failover chain) │ └────────┬───────────────┘ │ ┌─────┴────────────────────────────────────┐ │ │ ↓ │ ┌──────────────────┐ │ │ PRIMARY │──── OK ──── Responde ──► 📱│ │ openai-codex/ │ │ │ gpt-5.3-codex │ │ └──────┬───────────┘ │ │ FAIL │ ↓ │ ┌──────────────────┐ │ │ FALLBACK #1 │──── OK ──── Responde ──► 📱│ │ github-copilot/ │ │ │ claude-opus-4.6 │ │ └──────┬───────────┘ │ │ FAIL │ ↓ │ ┌──────────────────┐ │ │ FALLBACK #2 │──── OK ──── Responde ──► 📱│ │ copilot-proxy/ │ │ │ claude-sonnet-4.5│ │ └──────┬───────────┘ │ │ FAIL │ ↓ │ ┌──────────────────┐ │ │ FALLBACK #3 │──── OK ──── Responde ──► 📱│ │ anthropic/ │ │ │ claude-sonnet-4-5│ │ │ (Claude Code) │ │ └──────────────────┘ │ │ ◄─────────────────────────────────────────────

Resumen de lo que configuramosSummary of What We Configured

🦞 Setup Final🦞 Final Setup

Un agente de OpenClaw que es prácticamente indestructible:An OpenClaw agent that is practically indestructible:

ComponenteComponent Qué haceWhat It Does EstadoStatus
WhatsApp Channel Canal de comunicación principalMain communication channel ✓ CONNECTED
Watchdog Cron Revisa WhatsApp cada 15 min, reconecta si se caeChecks WhatsApp every 15 min, reconnects if it goes down ✓ ACTIVE
4 ProveedoresProviders Cadena de failover automática de modelosAutomatic model failover chain ✓ 4/4 OK
Node.js (Homebrew) Runtime estable a nivel de sistemaStable system-level runtime ✓ v22.14.0
Claude Code Keychain Credenciales Anthropic sin configuración manualAnthropic credentials without manual configuration ✓ Auto-detected

Con esta configuración, el agente puede sobrevivir:

With this configuration, the agent can survive:

El único escenario que no está cubierto es si los 4 proveedores fallan al mismo tiempo.The only scenario not covered is if all 4 providers fail at the same time. Spoiler: sí pasó. Sigue leyendo.Spoiler: it happened. Keep reading.

11. Post-Mortem: Cuando el Failover No Failoverea11. Post-Mortem: When Failover Doesn't Fail Over

Plot twist: después de configurar todo lo anterior, el bot se volvió a caer. Le mandaba mensajes por WhatsApp y respondía "Connection error." como texto literal. La cadena de failover estaba configurada... pero no funcionaba.

Plot twist: after configuring everything above, the bot went down again. I'd send messages via WhatsApp and it would respond with "Connection error." as literal text. The failover chain was configured... but wasn't working.

⚠️ El error real⚠️ The Real Error

Al revisar los logs del agente, encontré esto: FailoverError: No API key found for provider "anthropic". Auth store: ~/.openclaw/agents/main/agent/auth-profiles.json. Anthropic fallaba porque sus credenciales no estaban en el auth-profiles del agente.

Looking at the agent logs, I found this: FailoverError: No API key found for provider "anthropic". Auth store: ~/.openclaw/agents/main/agent/auth-profiles.json. Anthropic was failing because its credentials were not in the agent's auth-profiles.

¿Qué pasó?What Happened?

El problema fue que configuramos el modelo de Anthropic en openclaw.json (la config global), pero nunca copiamos las credenciales al archivo de autenticación del agente. OpenClaw separa la configuración de modelos de la autenticación:

The problem was that we configured the Anthropic model in openclaw.json (the global config), but never copied the credentials to the agent's authentication file. OpenClaw separates model configuration from authentication:

OpenAI y GitHub Copilot sí tenían sus credenciales ahí. Anthropic no. Entonces cuando OpenAI fallaba y GitHub Copilot estaba en cooldown por rate limit, el failover intentaba Anthropic... y también fallaba. El agente se quedaba sin opciones y respondía con el error como texto.

OpenAI and GitHub Copilot did have their credentials there. Anthropic didn't. So when OpenAI failed and GitHub Copilot was in cooldown from rate limiting, failover tried Anthropic... and that also failed. The agent ran out of options and responded with the error as text.

El fix: Registrar las credenciales de Claude Code en el agenteThe Fix: Register Claude Code Credentials in the Agent

Las credenciales de Claude Code viven en el macOS Keychain. Hay que copiarlas al auth-profiles.json del agente para que el failover pueda usarlas:

Claude Code credentials live in the macOS Keychain. They need to be copied to the agent's auth-profiles.json so failover can use them:

# 1. Extraer las credenciales del Keychain
security find-generic-password -s "Claude Code-credentials" -w

Esto devuelve un JSON con accessToken, refreshToken, y expiresAt. Luego hay que agregarlos al auth-profiles:

This returns a JSON with accessToken, refreshToken, and expiresAt. Then they need to be added to auth-profiles:

# 2. Agregar perfil de Anthropic en auth-profiles.json
# Archivo: ~/.openclaw/agents/main/agent/auth-profiles.json
# Agregar dentro de "profiles":

"anthropic:claude-code": {
  "type": "oauth",
  "provider": "anthropic",
  "access": "sk-ant-oat01-...",   // accessToken del Keychain
  "refresh": "sk-ant-ort01-...",  // refreshToken del Keychain
  "expires": 1757037189278         // expiresAt del Keychain
}

# Y agregar en "lastGood":
"anthropic": "anthropic:claude-code"

Después de agregar las credenciales, reiniciar el gateway:

After adding the credentials, restart the gateway:

openclaw gateway restart

El otro bug: el watchdog no encontraba openclawThe Other Bug: The Watchdog Couldn't Find openclaw

El cron watchdog ejecutaba openclaw gateway restart pero fallaba con zsh:1: command not found: openclaw. El gateway corre como un proceso de fondo (LaunchAgent) y su shell no tiene el mismo PATH que tu terminal.

The cron watchdog ran openclaw gateway restart but failed with zsh:1: command not found: openclaw. The gateway runs as a background process (LaunchAgent) and its shell doesn't have the same PATH as your terminal.

La solución: usar la ruta absoluta del binario en el mensaje del cron:

The solution: use the absolute path to the binary in the cron message:

# Antes (fallaba en background):
"Ejecuta 'openclaw gateway restart' para reconectarlo."

# Después (funciona siempre):
"Ejecuta '/ruta/completa/a/openclaw gateway restart' para reconectarlo."

✔ Lección aprendida✔ Lesson Learned

Configurar el failover tiene dos partes: (1) registrar los modelos en la config global, y (2) asegurarte de que las credenciales de cada proveedor estén en el auth-profiles.json del agente. Si falta cualquiera de las dos, el failover no funciona. Y para cron jobs, siempre usa rutas absolutas.

Configuring failover has two parts: (1) register models in the global config, and (2) make sure the credentials of each provider are in the agent's auth-profiles.json. If either is missing, failover doesn't work. And for cron jobs, always use absolute paths.

12. Checklist Final12. Final Checklist

Para asegurarte de que tu setup está completo y no te pase lo que a mí, revisa cada punto:

To make sure your setup is complete and you don't go through what I did, check each point:

Check ComandoCommand Qué verificarWhat to Verify
WhatsApp conectadoWhatsApp Connected openclaw channels status Status = CONNECTED
Modelo primario respondePrimary Model Responds openclaw agent -m "ping" --deliver Recibes respuesta en WhatsAppYou receive a response on WhatsApp
Fallbacks configuradosFallbacks Configured openclaw config get agents.defaults.model Primary + fallbacks listadosPrimary + fallbacks listed
Auth de CADA proveedorAuth for EACH Provider RevisarCheck auth-profiles.json Todos los fallbacks tienen credencialesAll fallbacks have credentials
Watchdog activoWatchdog Active openclaw cron list whatsapp-watchdog = ACTIVE
Watchdog usa ruta absolutaWatchdog Uses Absolute Path RevisarCheck cron/jobs.json Ruta completa al binario openclawFull path to openclaw binary

🦞 Para los que quieren ir más allá🦞 For Those Who Want to Go Further

Puedes agregar más canales (Telegram, Slack) como fallback de comunicación, configurar alertas por email cuando el watchdog detecta una falla, o incluso agregar proveedores locales (Ollama, LM Studio) como último-último recurso para funcionar completamente offline. Las posibilidades con OpenClaw son infinitas.

You can add more channels (Telegram, Slack) as communication fallback, configure email alerts when the watchdog detects a failure, or even add local providers (Ollama, LM Studio) as a last-last resort to work completely offline. The possibilities with OpenClaw are endless.

13. La Tormenta Perfecta: Los 3 Proveedores Cayeron a la Vez13. The Perfect Storm: All 3 Providers Went Down at Once

Recuerdan que dije que si los 3 proveedores fallan al mismo tiempo "tenemos problemas más grandes"? Bueno, pasó. Horas después de publicar este post, los 3 murieron simultáneamente:

Remember when I said if all 3 providers fail at the same time "we have bigger problems"? Well, it happened. Hours after publishing this post, all 3 died simultaneously:

💀 Error: All models failed (3)

github-copilot/claude-opus-4.6: cooldown (rate_limit)
anthropic/claude-sonnet-4-5-20250929: OAuth token refresh failed
openai-codex/gpt-5.3-codex: ChatGPT Go plan usage limit (~5936 min)

Tres fallas distintas, cada una por una razón diferente. Un rate limit, un token expirado, y un límite de plan. La combinación perfecta para dejar al bot completamente muerto.

Three distinct failures, each for a different reason. A rate limit, an expired token, and a plan limit. The perfect combination to leave the bot completely dead.

Root cause #1: Tokens de Anthropic expirados en el KeychainRoot cause #1: Expired Anthropic Tokens in the Keychain

Las credenciales de Claude Code en el macOS Keychain estaban expiradas desde septiembre 2025. Claude Code funciona en memoria con tokens frescos, pero no actualiza el Keychain cuando renueva sus tokens internamente. OpenClaw lee del Keychain → obtiene tokens viejos → OAuth refresh falla → proveedor muerto.

Claude Code credentials in the macOS Keychain had been expired since September 2025. Claude Code works in memory with fresh tokens, but doesn't update the Keychain when it renews its tokens internally. OpenClaw reads from Keychain → gets old tokens → OAuth refresh fails → provider dead.

# Verificar cuándo se modificó el Keychain:
security find-generic-password -s "Claude Code-credentials" -g 2>&1 | grep mdat
mdat=0x323032353039... (September 2025!)

Root cause #2: El cooldown exponencial en cascadaRoot cause #2: The Cascading Exponential Cooldown

Revisé el código fuente de OpenClaw y encontré la trampa. Cuando un proveedor recibe un error 429 (rate limit), OpenClaw pone el perfil de autenticación en cooldown exponencial:

I reviewed the OpenClaw source code and found the trap. When a provider receives a 429 error (rate limit), OpenClaw puts the authentication profile in exponential cooldown:

Error #Error # Cooldown EfectoEffect
1er error1st error 60 segundosseconds Pausa corta, tolerableShort pause, tolerable
2do error2nd error 5 minutosminutes Empieza a dolerStarting to hurt
3er error3rd error 25 minutosminutes Proveedor básicamente muertoProvider basically dead
4to+ error4th+ error 1 hora (máximo)hour (maximum) Game over

El problema: el cooldown es global por perfil. Si el gateway procesa múltiples mensajes de WhatsApp concurrentemente (o un cron job al mismo tiempo), cada request fallido incrementa el contador. En segundos, el cooldown escala de 60s a 1 hora, y todos los requests siguientes fallan instantáneamente sin siquiera intentar llamar al API.

The problem: cooldown is global per profile. If the gateway processes multiple WhatsApp messages concurrently (or a cron job at the same time), each failed request increments the counter. In seconds, cooldown escalates from 60s to 1 hour, and all subsequent requests fail instantly without even trying to call the API.

Además, descubrí en los logs que los "rate limits" de 23 segundos en realidad eran timeouts. El API de Anthropic se colgaba (en vez de devolver 429 instantáneo), el request expiraba, y OpenClaw lo clasificaba como rate limit con el comentario en el código: // Treat timeout as potential rate limit (Antigravity hangs on rate limit).

Additionally, I discovered in the logs that the 23-second "rate limits" were actually timeouts. The Anthropic API would hang (instead of returning an instant 429), the request would expire, and OpenClaw classified it as a rate limit with the code comment: // Treat timeout as potential rate limit (Antigravity hangs on rate limit).

Root cause #3: Config error que eliminó un proveedor de la cadenaRoot cause #3: Config Error That Removed a Provider from the Chain

Durante el debugging, cambié el modelo primario de openai-codex a anthropic para forzar pruebas directas. Esto causó que la cadena de failover pasara de 3 proveedores a 2:

During debugging, I changed the primary model from openai-codex to anthropic to force direct testing. This caused the failover chain to go from 3 providers to 2:

# Config durante debugging (MAL / WRONG):
"primary": "anthropic/claude-sonnet-4-5-20250929"    // anthropic como primario
"fallbacks": [
  "github-copilot/claude-opus-4.6",              // fallback 1
  "anthropic/claude-sonnet-4-5-20250929"          // duplicado = eliminado
]
// Resultado: solo 2 proveedores! openai-codex quedó fuera

# Config correcta / Correct config:
"primary": "openai-codex/gpt-5.3-codex"               // openai como primario
"fallbacks": [
  "github-copilot/claude-opus-4.6",              // fallback 1
  "anthropic/claude-sonnet-4-5-20250929"          // fallback 2
]
// Resultado: 3 proveedores en cadena

Los logs del gateway confirmaban: All models failed (2) — no 3. OpenClaw deduplica los modelos, así que anthropic como primario + anthropic como fallback = un solo proveedor.

The gateway logs confirmed: All models failed (2) — not 3. OpenClaw deduplicates models, so anthropic as primary + anthropic as fallback = a single provider.

El fix: Tokens frescos via OAuth PKCE + restaurar la cadenaThe Fix: Fresh Tokens via OAuth PKCE + Restore the Chain

La solución tuvo 3 partes:

The solution had 3 parts:

Parte 1: Obtener tokens frescos de Anthropic haciendo el flujo OAuth PKCE manualmente (el mismo que usa Claude Code internamente):

Part 1: Get fresh Anthropic tokens by doing the OAuth PKCE flow manually (the same one Claude Code uses internally):

# 1. Generar PKCE verifier + challenge
# 2. Abrir URL de autorización en claude.ai/oauth/authorize
# 3. Copiar el código del callback
# 4. Intercambiar código por tokens frescos:
curl -X POST https://console.anthropic.com/v1/oauth/token \
  -H "Content-Type: application/json" \
  -d '{"grant_type":"authorization_code","code":"...","code_verifier":"...","client_id":"...","redirect_uri":"..."}'

# Resultado: access_token + refresh_token frescos (8 horas)

Parte 2: Restaurar el modelo primario a openai-codex/gpt-5.3-codex para tener los 3 proveedores en la cadena.

Part 2: Restore the primary model to openai-codex/gpt-5.3-codex to have all 3 providers in the chain.

Parte 3: Limpiar los cooldowns acumulados en auth-profiles.json y reiniciar el gateway:

Part 3: Clear the accumulated cooldowns in auth-profiles.json and restart the gateway:

# Limpiar cooldowns (en auth-profiles.json, cambiar usageStats a vacío):
"usageStats": {}

# Reiniciar:
openclaw gateway restart

Resultado del test después del fix:

Test result after the fix:

openclaw agent --session-id "test-fix" -m "di OK"
[failover] openai-codex: rate_limit (10s) → skip
[failover] github-copilot: rate_limit (22s) → skip
[failover] anthropic: ✓ success
OK

✔ Bot restaurado✔ Bot Restored

Los 2 primeros proveedores siguen con rate limit temporal, pero el tercero (Anthropic con tokens frescos) responde correctamente. La cadena de failover funciona como debe: si los primeros fallan, el tercero entra.

The first 2 providers still have temporary rate limits, but the third (Anthropic with fresh tokens) responds correctly. The failover chain works as it should: if the first ones fail, the third one steps in.

14. Lecciones del Incidente14. Lessons from the Incident

  1. El Keychain miente. Claude Code funciona en memoria con tokens frescos pero no actualiza el Keychain. Si dependes del Keychain para credenciales, verifica que no estén expiradas.
  2. The Keychain lies. Claude Code works in memory with fresh tokens but doesn't update the Keychain. If you rely on the Keychain for credentials, verify they're not expired.
  3. El cooldown exponencial es una espada de doble filo. Protege contra spam al API, pero en un escenario con múltiples requests concurrentes, un solo error escala en segundos a 1 hora de bloqueo total.
  4. Exponential cooldown is a double-edged sword. It protects against API spam, but in a scenario with multiple concurrent requests, a single error escalates in seconds to 1 hour of total blocking.
  5. Nunca cambies el primary model para "debug rápido". Si tu primary es igual a un fallback, OpenClaw deduplica y pierdes un proveedor de la cadena sin darte cuenta.
  6. Never change the primary model for "quick debugging". If your primary equals a fallback, OpenClaw deduplicates and you lose a provider from the chain without realizing it.
  7. Los timeouts se clasifican como rate limits. Una respuesta lenta del API (23s timeout) cuenta igual que un 429 real. Esto acelera el cooldown exponencial artificialmente.
  8. Timeouts are classified as rate limits. A slow API response (23s timeout) counts the same as a real 429. This artificially accelerates exponential cooldown.
  9. Limpia usageStats después de arreglar la causa raíz. Si no limpias los cooldowns acumulados, el gateway sigue rechazando requests instantáneamente incluso con tokens frescos.
  10. Clean usageStats after fixing the root cause. If you don't clear the accumulated cooldowns, the gateway keeps rejecting requests instantly even with fresh tokens.

🦞 El setup actualizado🦞 The Updated Setup

Después de este incidente, la cadena de failover tiene 3 proveedores activos con tokens verificados, cooldowns limpios, y el primary model correctamente configurado. La próxima vez que un proveedor falle, los otros dos entran. Y si los 3 fallan... bueno, ya sé cómo arreglarlo en 5 minutos en vez de 5 horas.

After this incident, the failover chain has 3 active providers with verified tokens, clean cooldowns, and the primary model correctly configured. Next time a provider fails, the other two step in. And if all 3 fail... well, now I know how to fix it in 5 minutes instead of 5 hours.