本文由 Codex 根据实际排查过程整理生成。
最近我在用一台 Mac 上的 Codex App 控制另一台 Mac mini。入口是在 Codex App 的 Devices you can control from this Mac。Mac mini 端已经打开了远程控制,但本机尝试连接时一直失败,界面只给出一句比较笼统的错误:
Failed to update connection
这个问题表面看像网络问题,实际根因是本机保存了一份已经被服务端撤销的 remote-control client 授权。下面是完整的排查和解决过程。
现象
当时的状态大概是这样:
- Mac mini 上已经开启 remote control。
- 两台机器在同一网络环境中,SSH 可以正常连到 Mac mini。
- Mac mini 上 Codex App 和 app-server 都在运行。
- 本机 Codex App 的设备列表里能看到 Mac mini,但点击连接时报
Failed to update connection。 - 重启 Codex App、重新开关 Mac mini 端 remote control 后,问题仍然存在。
这说明问题不太像普通的网络不可达,也不像 Mac mini 端服务完全没起来。
第一轮排查:确认 Mac mini 端是否正常
我先确认 Mac mini 端的几个基础条件。
通过 SSH 可以连上:
1 | ssh mac-mini |
继续检查 Mac mini 上的 Codex 进程,可以看到 Codex App、app-server 以及相关服务都在运行。再看 Codex daemon 的配置:
1 | ~/.codex/app-server-daemon/settings.json |
其中:
1 | { |
Mac mini 本地的 Codex SQLite 状态库中也能看到 remote control enrollment 记录,说明 Mac mini 作为被控端已经注册过环境,并且服务侧能发现它。
这一轮的结论是:Mac mini 端大体正常,问题更可能出在发起控制的这台 Mac 上。
关键线索:本机日志里的 revoked client
真正的突破来自本机 Codex App 日志。
日志目录大致在:
1 | ~/Library/Logs/com.openai.codex/ |
在最新日志中搜索 connection 相关错误时,发现了关键报错:
1 | Remote-control client has been revoked |
同时还能看到本机正在尝试连接某个旧的 remote-control host:
1 | remote-control:env_e_... |
这条信息非常关键。它说明:
- 本机不是连不到 Mac mini。
- Mac mini 端也不是没有响应。
- 服务端明确拒绝了当前这个本机 remote-control client。
- 这个 client 已经被撤销,但本机 Codex App 仍然在尝试复用它。
换句话说,Failed to update connection 只是 UI 层的泛化错误,真实原因是本机保存了一个失效的控制端授权。
为什么简单删除状态不够
本机 Codex App 的全局状态文件里有 remote control 相关记录:
1 | ~/.codex/.codex-global-state.json |
里面包含:
- remote-control client enrollment
- 当前选中的 remote host
- 已添加的 remote-control environment id
- remote-control 自动连接状态
一开始我尝试直接清理这个 JSON 文件里的旧 enrollment。但问题是:Codex App 运行时会把全局状态读入内存,并在退出或状态更新时重新写回文件。
所以如果 Codex App 没有完全退出,直接改文件很容易被运行中的主进程覆盖。结果就是看起来删掉了,重开后旧 client 又回来了。
深入确认:device key 也要一起处理
为了确认 remote-control client 是如何保存的,我进一步检查了 Codex App 内部实现。
Codex App 的 Electron 资源里有 native 模块:
1 | remote-control-device-key.node |
它暴露了几个和设备密钥相关的方法:
1 | createDeviceKey |
也就是说,remote-control client 不只是 JSON 里的一段状态,还和本机保存的 device key 有关。旧 client 被服务端 revoke 后,如果本机继续拿同一个 client 和 key 去连接,就会一直被拒绝。
因此完整清理需要两部分:
- 删除本机
.codex-global-state.json里的旧 remote-control enrollment 和连接状态。 - 删除对应的本机 device key。
解决方案:在 Codex 完全退出后清理旧授权
最终有效的做法是:等待 Codex App 主进程完全退出后,再清理本机旧 remote-control 授权。
清理内容包括:
electron-remote-control-client-enrollmentsselected-remote-host-idadded-remote-control-env-idsremote-connection-auto-connect-by-host-id中所有remote-control:开头的条目- 与旧 enrollment 对应的 device key
清理前先备份:
1 | cp ~/.codex/.codex-global-state.json \ |
然后在 Codex App 完全退出后写回清理后的状态,并调用 Codex 自带的 native device-key 模块删除旧 key。这里涉及 Codex App 的内部状态,操作前一定要备份,并确认只清理旧 remote-control client 对应的 key;不确定时不要直接批量删除。
这一步完成后,再重新打开 Codex App。
验证结果
重开 Codex App 后,我做了几项验证。
本机状态已经变成干净状态:
1 | { |
清理日志显示,旧 client 和旧 key 都已经移除。这里不贴完整 ID,只保留占位符:
1 | removed client hash: <redacted> |
重新检查最新 Codex App 日志,之前的关键错误已经消失:
1 | Remote-control client has been revoked |
没有再出现。
同时,本机 app-server 初始化正常:
1 | initialize_handshake_result outcome=success |
Mac mini 端也再次确认:
remoteControlEnabled=true- Codex App 进程存在
- app-server 进程存在
- enrollment 表里仍能看到
mac-mini.local
这说明旧的 revoked client 问题已经解决。
最后一步:重新授权本机控制 Mac mini
清理之后,本机不再持有旧的 remote-control client,因此状态会变成“没有授权”。
这不是坏事,反而是预期结果。接下来需要在 Codex App 的 UI 里重新授权:
- 打开 Codex App 设置。
- 进入 Devices you can control from this Mac 或 Remote Connections 页面。
- 找到 Mac mini /
mac-mini.local。 - 点击 Add / Setup / Authorize。
- 完成 ChatGPT step-up 授权。
- 再点击 Connect。
重新授权后,本机会生成新的 remote-control client 和新的 device key。这个新 client 没有被 revoke,就可以正常连接 Mac mini。
结论
这次问题的根因不是 Mac mini 端没开,也不是 SSH 或局域网不通,而是控制端这台 Mac 保存了一个已经被服务端撤销的 remote-control client。
UI 上看到的:
1 | Failed to update connection |
只是表层错误。真正应该看的日志信号是:
1 | Remote-control client has been revoked |
解决思路可以总结成一句话:
让 Codex App 完全退出,清掉本机旧的 remote-control enrollment 和 device key,然后重新打开 Codex App,对 Mac mini 做一次新的授权。
排查这类问题时,最好把“被控端是否正常”和“控制端授权是否有效”分开看。前者看 Mac mini 的 remote-control 开关、app-server 和 enrollment;后者看本机 Codex 日志、global state 以及 device key。这样就不会被一个笼统的 Failed to update connection 带偏。