2026 OPENCLAW
UPDATE_
MAUVAIS_
NODE_
ÉCRIT.

Visuel abstrait comparant sortie terminal et versions du runtime Node

Autour de OpenClaw v2026.5.20, un mode de panne souvent pris pour une fausse mise à jour ou un jeton de canal cassé est en réalité plus simple et plus brutal : le chemin Node figé dans votre LaunchAgent ne correspond plus à celui que le shell résout via which node, parce que openclaw update a réécrit l’unité de service avec le Node présent sur le PATH pendant les étapes de suivi. Après le prochain gateway restart, launchd peut quitter toutes les quatre-vingt-dix secondes avec le code 1 tandis que la CLI affiche déjà la nouvelle semver. La release 2026.5.20 (PR #84043) impose le travail post-install via le Node de service Gateway géré et expose runningVersion dans openclaw gateway status --json pour repérer le décalage de protocole CLI/Gateway. Cet article fournit matrice de symptômes, tableau de décision, runbook en six étapes, trois portes d’acceptation, étude de cas et FAQ—avec liens vers nos articles sur la fausse MAJ et gateway status, les jetons LaunchAgent expirés et la config invalide et doctor—afin d’exécuter acceptation 7×24 et rollback sur une passerelle Apple Silicon distante sans deviner.

1. Anatomie du problème : pas un écart de version—une dérive du binaire Node

Premièrement : plusieurs installations Node sur un Mac sont la norme. Sur Apple Silicon typique : Node Homebrew sous /opt/homebrew/opt/node/bin/node, défaut nvm 22.x dans ~/.nvm/versions/node/..., alias global fnm 20.x—coexistants. Lors de openclaw gateway install, l’outil a capturé le chemin absolu du node actif dans ~/Library/LaunchAgents/ai.openclaw.gateway.plist comme premier élément de ProgramArguments. Ce chemin reste gelé jusqu’à réécriture—souvent silencieuse pendant une mise à jour.

Deuxièmement : un changement de PATH déclenche la dérive. Vous SSH, nvm use 25, lancez openclaw update. Avant 5.20, les hooks de suivi—doctor, réparation plugins, redémarrage Gateway—s’exécutaient sous le nouveau Node du PATH, pas celui que launchd utilisait encore. Si le préfixe npm global a migré, la plist pointe vers un shim nvm alors que la prod était sous Homebrew. Version tarball correcte—le processus supervisé ne démarre pas.

Troisièmement : distinguer de la fausse mise à jour. Là, la CLI indique 5.20 et le Gateway tourne encore en 5.12—le PID n’a pas changé de build. Ici, CLI et JSON peuvent tous deux dire 5.20, mais launchd plante car engines.node échoue sur le mauvais major, modules natifs incohérents, ou script openclaw sous un préfixe que le Node figé ne résout pas. Symptômes qui se chevauchent : échec handshake WebSocket, timeout RPC, unauthorized apparent—le correctif est l’alignement runtime, pas un autre npm install -g depuis le portable.

Quatrièmement : le décalage de protocole en 5.20. La release conserve un health check quand la CLI précède le Gateway d’un petit patch au redémarrage—si la dérive Node empêche le boot, vous voyez des boucles de crash, pas un diff de version propre. Capturer la plist avant d’accuser les jetons de canal.

Cinquièmement : surveiller les LaunchAgents updater hérités. L’issue #82167 documente ai.openclaw.update.* qui relancent et envoient SIGTERM au Gateway toutes les quelques minutes. À partir de 5.20, openclaw update les désactive en best-effort ; le runbook doit inclure launchctl list | grep openclaw pour séparer le thrash updater de la dérive Node.

Sixièmement : le Mac distant amplifie l’erreur. Validation sur MacBook avec nvm, Gateway sur Mac Studio sous compte de service Homebrew-only. Copier des fragments de plist ou lancer update depuis la session portable sans PATH figé—succès vendredi, panne totale samedi. Traiter chemin Node plist, npm root -g et which node comme un seul paquet de preuves par hôte.

Septièmement : modules natifs et arbres npm globaux aggravent les symptômes. OpenClaw et les plugins lient souvent des add-ons natifs à la version Node utilisée à l’installation. Si launchd démarre soudain un autre major, vous n’obtenez pas toujours immédiatement « Module not found »—parfois le Gateway crash au premier RPC ou au chargement d’un plugin canal, tandis que la CLI fonctionne encore dans un autre shell. Le proof reste la chaîne plist → node → npm root → racine package openclaw.

Distinction shell login vs launchd non-login : nvm et fnm vivent dans .zshrc ou .bashrc ; launchd démarre le job Gateway sans ces fichiers. Un update depuis SSH interactif peut écrire un chemin Node « normal » pour vous, mais isolé sous ~/.nvm/... pour launchd—chemin qui disparaît après rotation nvm au prochain restart du service.

2. Matrice de décision : aligner Node d’abord ou rollback du paquet ?

Signal terrainAction privilégiéeÀ éviter
openclaw --version et Node plist sur majors différentsGeler PATH → relancer update avec Node de service ou gateway install --forceÉditer ProgramArguments sans backup plist
gateway status --json sans runningVersiongateway restart --wait, puis code de sortie launchdSupprimer tout l’arbre ~/.openclaw
Telegram ne clignote qu’après MAJÉcarter dérive Node avant travail jeton canalMélanger avec correctifs timeout HTTP 5.2
Portable OK, Mac distant downDiff plist Node vs npm root -g par hôteCopier chemins nvm du portable dans plist Studio
Audit exige preuveRépéter six étapes + sonde 30 min sur Mac témoinMAJ prod vendredi peak sans fenêtre rollback

La matrice sert d’échelle de triage, pas de substitut aux preuves. runningVersion absent et Telegram instable—aligner Node (lignes un et deux) avant les credentials. Rollback npm sans plist reproduit le crash au prochain restart. Si plist et which node concordent en shell minimal mais Gateway échoue, basculer vers le runbook config invalide : schéma fail-closed et doctor sont orthogonaux mais peuvent s’empiler après une mauvaise nuit de MAJ.

3. Runbook terrain en six étapes

Étape 1 Geler le triplet de preuves

Avant tout correctif, archiver : openclaw --version ; which node et node -v ; les deux premiers ProgramArguments de ai.openclaw.gateway.plist ; le JSON complet de openclaw gateway status --json avec runningVersion (5.20+). Si les chemins divergent déjà, étiqueter dérive Node—rotation de jeton seulement après l’étape 3.

Étape 2 Analyser engines.node

Lire package.json sous $(npm root -g)/openclaw/ : engines.node et notes de release 2026.5.20. Si le major du Node de service est sous le seuil, monter cet arbre Node d’abord, puis openclaw. Doctor peut être vert en shell interactif tandis que launchd invoque un binaire figé plus ancien—engines.node est le contrat unattended.

Étape 3 Aligner le Node Gateway géré (chemin 5.20)

Sur 2026.5.20+, exécuter openclaw update depuis un shell PATH minimal ou profil compte de service. L’updater doit préférer le Node figé du LaunchAgent pour le suivi. Déjà dérivé : openclaw gateway install --force avec le Node voulu—un gestionnaire, un préfixe global, une plist. npm root -g doit correspondre aux ProgramArguments. Re-snapshot étape 1 ; chemins identiques avant restart.

Étape 4 Nettoyer les LaunchAgents updater hérités

launchctl list | grep openclaw. Si ai.openclaw.update.* avec relances fréquentes, update 5.20 doit le désactiver ; sur builds anciens, launchctl bootout gui/$UID/ai.openclaw.update.* après vérification qu’aucun job n’est en cours. Issue #82167 : SIGTERM Gateway toutes les trois minutes souvent updater vs unité principale—pas l’API modèle par défaut.

Étape 5 Redémarrage Gateway ordonné et sondes JSON

openclaw gateway restart --force --wait, puis trois fois openclaw gateway status --json espacées de dix secondes. Exiger runningVersion stable, RPC dans le SLO, pas d’exit launchd croissant. Sans session interactive : launchctl kick -k gui/$UID/ai.openclaw.gateway, sondes depuis bastion. Joindre JSON au ticket.

Étape 6 Contrôle distant 7×24 et fenêtre rollback

Sur nœud témoin—idéalement Mac distant MACGPU PATH propre sans nvm dans launchd—répéter étapes 1–5 à l’identique. Seulement après 30 minutes de openclaw channels status --probe vert toucher la prod. Échec prod : épingler tarball openclaw précédent et restaurer l’ancienne paire Node plist ; diff préfixe npm et plist avant clôture.

# Snapshot triplet openclaw --version which node && node -v plutil -p ~/Library/LaunchAgents/ai.openclaw.gateway.plist | head -20 openclaw gateway status --json # Alignement préfixe global npm root -g ls -la "$(npm root -g)/openclaw/package.json" grep -A2 '"engines"' "$(npm root -g)/openclaw/package.json" openclaw gateway install --force openclaw gateway restart --force --wait openclaw gateway status --json launchctl list | grep openclaw

4. Trois portes d’auto-contrôle

Porte A — Node : Chemin Node plist existe ; node -v satisfait engines.node ; même chemin que which node en shell de service sans nvm/fnm—sauf si ces hooks sont le choix prod explicite dans la plist.

Porte B — Version : gateway status --json rapporte runningVersion alignée avec openclaw --version dans la fenêtre de skew documentée par 5.20—même runtime Node pour les deux.

Porte C — Canaux : openclaw channels status --probe sans lignes rouges ; 30 minutes post-restart exit launchd 0, pas de motif « sortie immédiate après boot » dans les logs Gateway. Échec porte → retenir trafic prod, rollback plist + pin paquet.

5. Cas profond : « update réussi, Gateway offline au matin »

« Ops ont lancé openclaw update depuis un MacBook nvm Node 22 en SSH vers un Mac Studio Gateway sous Homebrew Node 25. Logs vendredi : succès. Plist samedi sur /Users/ops/.nvm/.../node, plugins attendaient /opt/homebrew/... ; launchd exit 90s code 1. »

L’équipe ouvre d’abord le runbook fausse MAJ—plugins parfois OK en SSH manuel. CLI et JSON 5.20—jusqu’au diff plist vs which node sur compte service Studio. Dérive Node, pas jeton. Répétition sur Mac témoin MACGPU un seul Homebrew Node, six étapes, JSON vert—prod reçoit seulement paire Node plist + gateway install --force ; canaux verts en trente minutes. Règle post-incident : jamais openclaw update depuis shell nvm contre Gateway launchd distant ; profil service, env -i, ou CI Node figé.

Seconde leçon : diff plist et capture engines.node au ticket de MAJ. Les auditeurs demandent preuve de pin runtime, pas seulement captures semver. La répétition sur nœud témoin a évité un second collage aveugle de plist depuis le portable.

6. Regard secteur : épinglage runtime et pratique daemon macOS

En 2026, fragmentation Node—nvm, fnm, Volta, Homebrew—fait coexister « CLI globale » et « runtime daemon » sur le même UID. OpenClaw 5.20 intégrant le managed service Node à la chaîne update marque un shift : ops passerelle Agent de « installé » vers pin, diff, rollback. Les modèles de changement incluent chemin Node plist, validation engines.node, JSON gateway status avec la semver.

launchd et ses chemins absolus punissent la dérive plus durement que systemd Linux avec bloc Environment propre. Une mauvaise chaîne node = panne totale. Mac Apple Silicon distants—Studio, mini—populaires pour passerelles 7×24 (mémoire unifiée, stacks canal desktop) ; l’hygiène PATH coûte cher sans couche hyperviseur.

Louer un Mac distant MACGPU comme plan de contrôle doré isole la répétition du profil nvm portable. Snapshot disque, six étapes, sonde 30 min, puis promotion exacte du couple plist/préfixe npm en prod—assurance opérationnelle contre « ça marche dans ma session SSH ».

Windows et Linux connaissent aussi le multi-Node ; la leçon LaunchAgent macOS : gateway install --force comme outil realign supporté, pas XML à la main. Les équipes qui l’internalisent réduisent les récidives après chaque train micro-release de mai.

En environnement régulé, un Golden Image sur Mac distant dédié—un seul gestionnaire Node, préfixe global documenté, snapshot hebdomadaire de plist—paye rapidement. Chaque micro-release OpenClaw s’y répète d’abord ; le ticket joint JSON gateway status --json, diff plist et sonde canal 30 minutes. La prod n’exécute que les commandes approuvées, jamais un nvm use improvisé depuis le portable. Les nœuds MACGPU conviennent comme copie dorée car PATH et contexte launchd visent l’exploitation unattended dès le départ.

À terme, l’exploitation Agent passe de « npm global suffit » à runtime-as-contract. Ancrer cela tôt dans les runbooks évite d’escalader vers fournisseurs de modèles ou équipes jetons canal alors que la cause n’a jamais été réseau.

7. Seuils numériques citables

① Chemin Node plist ≠ which node en shell service → ne pas déclarer MAJ réussie. ② engines.node exige Node ≥22 (vérifier tarball)—Node service sous seuil → monter Node d’abord. ③ Plus de trois exits launchd non nuls en 30 min post-MAJ → rollback par défaut avec diff plist/npm conservé. ④ Trois appels gateway status --json sans runningVersion → Gateway Unhealthy. ⑤ Préfixe npm global différent distant vs portable → interdire copie fragment plist cross-machine ; régénérer avec gateway install --force par hôte.

Hygiène ops : sérialiser installs npm globales par UID ; exporter dix minutes de logs Gateway avant/après restart ; backup plist horodatée au ticket—secondes de travail, heures de panne canal évitées, fournisseurs modèle hors cause jusqu’à épuisement des preuves Node.

8. FAQ

Différence avec fausse MAJ ? Fausse MAJ compare chaînes de version et âge PID quand CLI a bougé sans processus. Dérive Node compare chemins binaires et engines.node quand versions concordent mais launchd n’exécute pas le runtime figé.

Faut-il 5.20 ? Correctif managed service Node dans 5.20 (PR #84043). Builds antérieurs : gateway install --force manuel après chaque update avec PATH figé—or monter d’abord en 5.20.

Docker ? Image avec un Node en général ; dérive rare. Surveiller tag image vs état volume monté.

Éditer plist à la main ? Possible avec backup ; gateway install --force régénère ProgramArguments cohérents.

Rôle nœud MACGPU ? PATH isolé, Apple Silicon 7×24, répétition contrôle des six étapes et fenêtre rollback—ne remplace pas l’approbation de changement.

doctor --fix aide ? Doctor répare schéma config, pas chemins Node. Lancer doctor après portes Node si fail-closed persiste—ordre dans runbook config invalide.