Codex 切换账号后卡住,最后我是这样修的
摘要: 作者分享了修复Codex切换账号后卡住问题的经验。问题表现为token刷新失败、重连卡顿及会话文件路径冲突。通过分析发现Codex存在三类状态:账号状态、运行状态和对话记录。正确解决方法是: 清理账号状态(auth.json等)和残留进程 保留有价值的会话文件(.codex/sessions) 确认网络连接正常 创建自动化脚本实现"干净启动",包括关闭残留进程、备份账号
Codex 切换账号后卡住,最后我是这样修的
这次遇到的问题很典型。
我在 Codex 里切换账号后,先看到 token 刷新失败:
Your access token could not be refreshed because you have since logged out or signed in to another account.
我当时第一反应其实很朴素,哦,登录态过期了,重新登一下就行。
结果不是。
重新登录后,又开始卡在:
Reconnecting... 5/5
再往后,恢复旧对话时出现了这个报错:
cannot resume running thread <THREAD_ID> with stale path:
requested `C:\Users\<USER>\.codex\sessions\...\rollout-...jsonl`,
active `\\?\C:\Users\<USER>\.codex\sessions\...\rollout-...jsonl`
看到这里我就有点烦了。因为这种错误最麻烦,它不像一个明确的红灯,而像一堆半亮不亮的灯。账号像有问题,网络像有问题,本地文件也像有问题。
这个问题的本质不是“对话文件坏了”,而是切账号时有几层状态混在一起了。
Codex 本地大概有三类状态。
第一类是账号状态,比如:
C:\Users\<USER>\.codex\auth.json
%APPDATA%\Codex\Network
%APPDATA%\Codex\Local Storage
第二类是运行状态,比如某个 thread 是否还被认为正在 running。
第三类才是你的真实对话记录:
C:\Users\<USER>\.codex\sessions\YYYY\MM\DD\rollout-xxx.jsonl
我一开始差点把第三类也当缓存清掉,这是不对的。真正要清的是账号状态和残留运行状态,对话文件要保留。
具体排查时,我先确认网络是否正常:
Resolve-DnsName api.openai.com
Resolve-DnsName chatgpt.com
Test-NetConnection api.openai.com -Port 443
Test-NetConnection chatgpt.com -Port 443
如果你用了代理或 TUN,看到 198.18.x.x 这类 fake-ip,并且 TcpTestSucceeded 是 True,说明网络基本没问题。
这里我松了一口气。至少不是 Codex 整个坏了,也不是账号完全不可用。网络这一层通了,剩下就是本地状态的问题。
然后查 Codex 是否还有残留进程:
Get-Process | Where-Object {
$_.ProcessName -match 'Codex|codex'
} | Select-Object ProcessName,Id,Path
再确认出问题的旧会话文件是不是已经完成:
Get-Content "C:\Users\<USER>\.codex\sessions\YYYY\MM\DD\rollout-xxx.jsonl" -Tail 1
如果最后能看到类似:
{"type":"event_msg","payload":{"type":"task_complete"}}
说明这个对话本身是完整的,不该删除。
这个发现挺关键的。因为我一开始也差点把它当成坏会话移走,后来才意识到,用户真正想要的不是“别报错”,而是“这个旧对话还能继续用”。这两个目标差很多。
最后我把修复流程收敛成了一个脚本。核心思路是:
关闭残留 Codex 进程。
备份并移走旧账号登录态。
清 Electron Cookie、Local Storage、Network 缓存。
保留 .codex\sessions 里的所有对话。
下面是脱敏后的脚本核心版本:
$ErrorActionPreference = "Stop"
$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
function Backup-Path($path, $stamp) {
if (Test-Path -LiteralPath $path) {
Move-Item -LiteralPath $path -Destination "$path.bak-$stamp"
Write-Host "Backed up: $path"
}
}
# 1. 停掉 Codex 残留进程
Get-Process |
Where-Object { $_.ProcessName -match "Codex|codex" } |
ForEach-Object {
Write-Host "Stopping $($_.ProcessName) pid=$($_.Id)"
Stop-Process -Id $_.Id -Force
}
# 2. 清账号状态,不动 sessions
$codexHome = Join-Path $env:USERPROFILE ".codex"
$roamingCodex = Join-Path $env:APPDATA "Codex"
Backup-Path (Join-Path $codexHome "auth.json") $stamp
Backup-Path (Join-Path $codexHome "models_cache.json") $stamp
Backup-Path (Join-Path $codexHome "cap_sid") $stamp
foreach ($name in @(
"Network",
"Local Storage",
"Session Storage",
"IndexedDB",
"Cache",
"Code Cache",
"GPUCache",
"blob_storage",
"lockfile"
)) {
Backup-Path (Join-Path $roamingCodex $name) $stamp
}
Write-Host "Preserved conversations:"
Write-Host (Join-Path $codexHome "sessions")
如果某个旧 thread 已经被误移走,也可以恢复回去。注意这里是复制回 sessions,不是删除备份:
$threadId = "<THREAD_ID>"
$codexHome = Join-Path $env:USERPROFILE ".codex"
$sessionRoot = Join-Path $codexHome "sessions"
$source = Get-ChildItem $codexHome -Directory -Filter "stale-sessions-*" |
Sort-Object LastWriteTime -Descending |
ForEach-Object {
Get-ChildItem $_.FullName -File -Filter "*$threadId*.jsonl"
} |
Select-Object -First 1
if ($source.Name -match "^rollout-(\d{4})-(\d{2})-(\d{2})T") {
$targetDir = Join-Path $sessionRoot "$($Matches[1])\$($Matches[2])\$($Matches[3])"
New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
Copy-Item $source.FullName -Destination (Join-Path $targetDir $source.Name)
}
为了以后切账号时不再手动跑命令,我又做了一个“干净启动 Codex”的脚本:
$cleanupScript = "C:\Path\To\fix-codex-reconnecting.ps1"
$codexExe = "C:\Path\To\Codex.exe"
& powershell -NoProfile -ExecutionPolicy Bypass -File $cleanupScript -KillCodex -PrepareAccountSwitch
Start-Sleep -Seconds 1
Start-Process -FilePath $codexExe
再给它建一个桌面快捷方式:
$desktop = [Environment]::GetFolderPath("Desktop")
$shortcutPath = Join-Path $desktop "Codex 干净启动.lnk"
$script = "C:\Path\To\start-codex-clean.ps1"
$wsh = New-Object -ComObject WScript.Shell
$shortcut = $wsh.CreateShortcut($shortcutPath)
$shortcut.TargetPath = "powershell.exe"
$shortcut.Arguments = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Minimized -File `"$script`""
$shortcut.WorkingDirectory = Split-Path -Parent $script
$shortcut.Description = "Clean Codex account state, preserve conversations, then launch Codex"
$shortcut.Save()
之后切账号就很简单了。
不要直接打开原来的 Codex。
点桌面的 Codex 干净启动,它会先清掉旧账号状态,再启动 Codex。历史对话还在,新账号也不会继承上一个账号残留的 running thread。
这个方案不算优雅,但它让我安心。因为它没有碰真正有价值的东西,只是在每次切账号前,把桌面端最容易残留的那几层状态清干净。
总结一下,这个问题不要用“删除整个 .codex 目录”来解决。
正确做法是:
清 token、Cookie、Local Storage、缓存、残留进程。
保留 .codex\sessions。
如果看到 stale path,优先判断是不是旧进程和旧运行态没释放,而不是直接删会话。
这才是这次踩坑里最有价值的部分。
说白了,AI agent 桌面应用已经不像以前那种“一个配置文件加一点缓存”的软件了。它有账号,有 websocket,有本地会话,有运行中的 thread。切账号这件小事,背后其实是在切一整套运行上下文。
所以别一上来就重装。
先把状态分层。
人会冷静很多,工具也会听话很多。
更多推荐



所有评论(0)