1. 卡在“安装中”的到底是哪一步
Codex 这类 AI 编码助手扩展更新时,进度条停在“正在安装”,然后就不动了,重启编辑器重试三次还是同一个位置——这个场景我遇到不止一次,也在不同机器上见过完全不同的成因。有人是网络侧没拿到安装包,有人是本地文件被占用写不进去,还有人是扩展宿主进程僵在那儿,界面显示的“安装中”其实早就结束了。所以第一件事不是急着删目录、清缓存,而是先把“安装”这件事拆开看清楚:它其实是四个串行阶段,卡在不同阶段,处理方式完全不一样。
我这里说的 Codex 扩展,指的是装在编辑器里的 AI 编码助手类插件。这类插件的共同特点是体积大(往往带本地运行时、语言服务、甚至二进制文件)、常驻后台进程多、更新频率高。这三个特点凑在一起,就让“更新卡住”成了高频问题。你要是装个几百 KB 的主题插件,基本不会遇到这种事。
1.1 从点击更新到真正可用:四个阶段的拆解
第一阶段:解析清单。编辑器向扩展市场请求该扩展的元数据,拿到最新版本号、兼容的编辑器版本区间(engines.vscode)、以及安装包的下载地址。这一步很快,通常是毫秒级,但它是“先决条件”——如果元数据拿不到,界面上根本不会显示“更新”按钮,或者点了之后马上报错。
第二阶段:下载安装包。把.vsix(本质是个 zip 包)下载到本地临时目录。AI 类插件这一步最容易出问题,因为包体动辄几十到两百多兆,弱网环境下一两分钟没进展,界面看起来就像死了一样。而且很多编辑器的下载环节重试策略比较保守,超时后不会立刻提示失败,只是静静挂着。
第三阶段:解压写入。把.vsix解开,落到扩展目录下形如codex-ai.codex-1.2.3的新文件夹里。这一步涉及大量小文件写入,是杀毒软件实时扫描、磁盘空间不足、文件句柄占用这几类问题的高发区。
第四阶段:注册与切换。更新extensions.json,把旧版本目录标记进.obsolete文件(编辑器不会立刻删旧目录,因为旧版本可能正被加载着),然后提示你“重新加载窗口”。重载之后,扩展宿主才会真正加载新版本。
提示:绝大多数“卡在安装中”的案例,卡点集中在第三和第四阶段。因为这两个阶段的失败往往不产生用户可见的错误弹窗,只在日志里留一行 warning。
1.2 进度条不动,最常见的三处阻塞
第一处是下载中断但没触发超时。表现是网络指示灯正常、其他网页能打开,但这个扩展的包就是下不下来。这类情况在跨区域访问扩展市场时尤其常见,企业内网出口策略、DNS 解析到较慢的节点、系统时间偏差导致 TLS 握手失败,都会造成这个现象。
第二处是文件句柄占用。这是 Windows 上最典型的坑。Codex 这类插件通常会拉起一个本地服务或语言服务子进程,进程名未必带扩展名,任务管理器里看着像一堆无关的node.exe。你点“更新”的时候,扩展宿主还在跑,子进程还攥着node_modules里某个.node原生模块的句柄,于是新版本文件写不进去,写入失败 → 重试 → 再失败,界面就一直停在“安装中”。
第三处是旧目录清理不掉导致的注册失败。编辑器的机制是:更新时先把旧目录重命名为待删除状态并记进.obsolete,新版本装好后再在下次启动时清理。如果旧目录里有文件杀不掉(还是句柄问题),或者.obsolete这个文件本身内容损坏、权限异常,注册阶段就会失败,而界面上呈现的仍然是“安装中”这种模糊状态。
2. 先看日志再动手:三分钟定位卡点
我见过太多人一上来就删extensions目录,结果把几十个插件的配置和本地缓存全清了,问题还不一定解决。正确的顺序是:先花两三分钟确认卡在哪一环,再动手。判断依据其实就两样——日志和文件时间戳。
2.1 三个必须打开的日志入口
入口一:扩展宿主日志。命令面板里搜“显示扩展日志”或“Developer: Open Extension Logs Folder”,会直接跳到日志目录。不同平台路径不一样:
| 平台 | 日志根目录 |
|---|---|
| Windows | %APPDATA%\Code\logs\ |
| macOS | ~/Library/Application Support/Code/logs/ |
| Linux | ~/.config/Code/logs/ |
进到最新的日期时间戳文件夹里,重点看main.log、sharedprocess.log,以及exthost子目录下的输出。扩展的实际安装动作是由共享进程(shared process)执行的,所以sharedprocess.log里往往有最直接的一行——比如下载超时、解压失败、EPERM、EBUSY、ENOSPC这类错误码。看到ENOSPC就是磁盘满了,看到EPERM基本是权限或杀软,看到EBUSY就是文件被占用。
入口二:输出面板里的扩展通道。打开输出面板,右上角下拉里选“扩展宿主”或者 Codex 自己的日志通道。这里能看到扩展自身在启动时打的日志,如果它每次都在某个初始化步骤报错然后退出,你就能判断问题不在安装而在加载。
入口三:带 trace 级别启动。命令行执行:
code --verbose --log trace这样启动的窗口会把更细粒度的日志写进当次会话的日志目录。我一般在前面两个入口看不到有效信息时才用它,因为 trace 日志量大,翻起来累。
2.2 用文件时间戳区分“没下下来”还是“写不进去”
这一步很关键,而且不需要任何工具。打开扩展目录:
# Windows explorer %USERPROFILE%\.vscode\extensions # macOS / Linux ls -la ~/.vscode/extensions然后看三件事:
- 有没有出现新版本号命名的目录(比如原来只有
codex-ai.codex-1.2.2,现在多了一个codex-ai.codex-1.3.0)。 - 目录里的文件数量和时间戳是不是停在某个时刻不动了。
- 有没有
.obsolete文件,里面的内容是什么。
如果压根没有新版本目录,说明卡在下载阶段,问题在网络侧;如果有新版本目录但内容残缺、时间戳不动,说明卡在解压写入,问题在本地磁盘、权限或占用;如果新版本目录完整、.obsolete里还留着旧版本记录但注册没完成,说明卡在第四阶段。
.obsolete是个 JSON 文件,长这样:
{ "codex-ai.codex-1.2.2": true }这个文件是编辑器“延迟删除”机制的账本。里面的键是待删目录名。如果这里的记录长期不消失,或者你发现同一个扩展的多个旧版本都堆在里面,就说明清理链路一直没跑通。
2.3 一张可以直接照着走的排查决策表
我把上面这些整理成一张表,遇到“卡在安装中”按顺序对照着走,比凭感觉乱试高效得多:
| 现象 | 大概率原因 | 快速验证 | 处理方向 |
|---|---|---|---|
| 无新版本目录,日志有超时 | 下载阶段失败 | sharedprocess.log里搜 timeout | 换网络环境或用离线包 |
| 有新目录但文件数明显偏少 | 解压写入中断 | 对比新旧目录文件数 | 彻底退出编辑器后重试 |
日志出现EBUSY/EPERM | 句柄占用 / 权限 | 任务管理器看残留 node 进程 | 结束进程 + 检查权限 |
日志出现ENOSPC | 磁盘空间不足 | 看扩展目录所在分区剩余空间 | 清理空间 |
| 新目录完整但一直“安装中” | 注册阶段失败 | 检查.obsolete与extensions.json | 修 JSON 或手动装 VSIX |
| 远端环境更新卡住 | 扩展装在远端 | 看是否连着远程会话 | 到远端清目录 |
3. 六种真实场景下的修复手段
定位完卡点,处理起来其实不难。下面这几种是我在实际环境里反复验证过的做法,按“从轻到重”的顺序排。
3.1 彻底退出:比“关窗口”强得多的一步
很多人以为点了窗口右上角的叉就叫退出。对 Codex 这类扩展来说,这不成立——扩展宿主可能还在后台跑,插件的本地服务进程也可能活着。Windows 上最稳的做法是任务管理器里确认Code.exe无残留;macOS 用Cmd+Q而不是点红叉;Linux 下pkill -f code之后再确认一遍。
更进一步,你可以用禁用扩展的方式启动一次,让编辑器不加载任何扩展,从而释放所有句柄:
code --disable-extensions在这个干净实例里再做一次更新。这时候扩展宿主不加载 Codex,也就没有子进程占文件,写入成功率会大幅提高。这是我最常用的一招,解决过至少四五台机器上的同类问题。
3.2 清理残留目录的正确姿势
确认编辑器完全退出后,进扩展目录,找三类东西:旧的.obsolete记录、残缺的新版本目录、以及形如.xxx-临时随机串的临时目录。把它们删掉,但保留extensions.json(除非它本身坏了)。
# macOS / Linux 示例:先看清楚再删 ls -la ~/.vscode/extensions | grep -E "codex|obsolete" rm -rf ~/.vscode/extensions/codex-ai.codex-1.3.0 # 只删残缺的那个 rm -f ~/.vscode/extensions/.obsoleteWindows 上用 PowerShell:
Get-ChildItem "$env:USERPROFILE\.vscode\extensions" -Filter "*codex*" | Select-Object Name, LastWriteTime Remove-Item "$env:USERPROFILE\.vscode\extensions\codex-ai.codex-1.3.0" -Recurse -Force Remove-Item "$env:USERPROFILE\.vscode\extensions\.obsolete" -Force注意:删
.obsolete是安全的,它只是一份待删账本;但删extensions.json会让编辑器丢失“哪些扩展被禁用”的状态记录,多数扩展会自动重新扫描回来,少数需要手动重装。所以我一般只在确认它损坏时才动它。
如果extensions.json确实坏了(比如文件是 0 字节,或者 JSON 语法错误),可以先把它改名备份,重启编辑器让它重建:
mv ~/.vscode/extensions/extensions.json ~/.vscode/extensions/extensions.json.bak3.3 手动安装 VSIX:最干脆的一条路
更新流程走不通,最直接的办法是绕开它——去扩展市场页面手动下载对应版本的.vsix,然后用命令行安装。这条路的好处是把“下载”和“安装”两个环节彻底解耦,出问题能立刻知道是哪一环。
# 覆盖安装(已存在同 ID 扩展时加 --force) code --install-extension /path/to/codex-ai.codex-1.3.0.vsix --force # 装完确认版本 code --list-extensions --show-versions | grep -i codex如果是想把新版退回到旧版(新版有兼容问题),也是同样操作,只是换成旧版本的 vsix 包。我建议每次更新前把当前能用的版本号记一下,真出问题时能一分钟内退回去。这个习惯救过我不少次——尤其是那种新版本要求更高编辑器版本、装上之后直接变成“已禁用”状态的情况。
顺带说一个容易被忽略的点:--install-extension走的是同一个安装链路,如果底层还是被句柄占用,它同样会失败。但它的好处是报错是显式的,终端里会直接告诉你EPERM还是EBUSY,不像图形界面那样只会转圈。
3.4 远程环境:扩展其实装在另一台机器上
连着远程开发会话的时候更新 Codex,界面卡住但本地怎么清都没用——因为扩展装的是远端那一侧。远端的扩展目录在~/.vscode-server/extensions(不同工具链路径略有差异,通常在同级目录下)。
这种情况下要做的是:断开远程连接 → 在远端机器上清理~/.vscode-server/extensions里的残缺目录 → 重新连接。远端的磁盘空间、权限、以及远端机器上的网络出口,才是这次更新的真实瓶颈。我有一次排查了半小时本地,最后发现是远端容器里磁盘只剩几十兆。
3.5 权限、杀软与“写入被拦”
日志里出现EPERM、EACCES,基本就是这两类:
一是目录权限异常。常见于把配置目录放在受管设备策略限制的位置,或者用管理员身份跑过编辑器之后,普通用户身份反而写不进去。检查办法是手动在那个目录里新建一个文本文件试试。
二是实时防护拦截。AI 类扩展的解压过程会短时间内落大量小文件,其中还可能有.node这类原生模块,很容易被启发式规则盯上。表现是解压到某个文件后卡住,日志里没有明确错误,只是时间戳不再更新。处理方式是给扩展目录加白名单,或者在更新期间临时关闭实时防护。前一种更省事,一次配好长期受益。
3.6 “假安装”:其实早就装好了
还有一种挺气人的情况:扩展目录里新版本文件齐全,extensions.json也更新了,但界面上的状态还挂着“安装中”。这通常是扩展宿主进程僵死导致的界面状态没刷新。
判断方法很简单:看命令面板里能不能看到新版本带来的新命令,或者直接看code --list-extensions --show-versions输出的版本号。如果已经是新版,那就只是状态没同步——重载窗口(命令面板搜“重新加载窗口”)基本能解决。真不行就重启一次编辑器,别去做多余的清理。
4. 网络侧卡点的判断边界
下载阶段的问题处理起来要谨慎一点,因为很容易误判。我给自己定了一条规矩:先确认是“慢”还是“断”,再决定要不要换方案。
4.1 分清“下载慢”与“根本连不上”
“慢”的表现是:sharedprocess.log里能看到持续的心跳或分段进度,磁盘上临时目录里的大小在缓慢增长。“断”的表现是:日志里出现ETIMEDOUT、ECONNRESET、getaddrinfo ENOTFOUND这类明确的失败码,或者干脆一行进展都没有。
这两者的处理完全不同。慢的话,最有效的办法是给足时间,别在中途频繁点“取消”再重试——反复中断会让临时文件反复重下,反而更慢。断的话,就要换网络出口(比如切到手机热点试一次),或者改用离线安装包。
还有一个隐蔽因素:系统时间偏差过大会导致 TLS 握手失败,报错看起来像网络问题,实际是证书校验不通过。看到证书相关字样时,先校准系统时间。
4.2 离线包落地:内网环境的标准做法
在出口受限的环境里,最可靠的方案永远是离线包。流程是:在一台能正常访问扩展市场的机器上下载对应版本.vsix→ 拷进内网 →code --install-extension xxx.vsix。
如果是团队统一环境,可以把 vsix 放到内部制品库,配一个统一的安装脚本:
#!/usr/bin/env bash set -e VSIX_DIR="./extensions" for f in "$VSIX_DIR"/*.vsix; do echo "installing $f" code --install-extension "$f" --force done这个脚本我们内部用来做开发机初始化,好处是版本完全可控,谁装都是同一个版本,不会再出现“我这能跑你那不能跑”的扯皮。而且首次安装和后续更新走同一套逻辑,不用两套心智。
5. 让下次更新别再卡:可以提前做的事
处理完当下的问题,值得花十分钟把这类坑堵掉。以下是我自己机器上固定会做的几件事,都是踩坑踩出来的。
5.1 更新前的一份自查清单
| 检查项 | 判断方式 | 不达标的处理 |
|---|---|---|
| 磁盘剩余空间 | 扩展目录所在分区剩余 | 留出至少安装包体积 3 倍空间 |
| 后台残留进程 | 任务管理器 /ps aux | 结束相关进程 |
| 当前版本号 | code --list-extensions --show-versions | 记下来,用于回退 |
| 是否有远程会话 | 左下角状态 | 先断开再更新 |
| 实时防护白名单 | 安全软件设置 | 把扩展目录加进去 |
.obsolete是否堆积 | 看文件内容 | 清理并重启 |
这份清单我贴在团队文档里,新同事第一次遇到卡住,对着走一遍基本就解决了,省下来回问人的时间。
5.2 自动更新还是手动更新
编辑器默认可能会自动更新扩展。对 Codex 这类核心生产力插件,我的选择是关掉自动更新、改成手动。理由很实际:自动更新发生在你最不想被打断的时候(比如正在改一段复杂逻辑),一旦卡住或者新版有兼容问题,代价是整段时间报废。手动更新的话,你可以挑一个有空处理问题的时机来点。
具体到版本策略,我的习惯是:大版本不第一时间跟,等一周看看社区反馈;小版本随便更。这和装系统补丁的心态差不多,用不上多高深的理论,纯粹是经验。
5.3 几个反直觉的坑
第一个坑:“重试”不一定有用,反而有害。反复点更新按钮,会在临时目录里堆出一堆半截的包,清理起来更麻烦。卡住超过两三分钟,就应该停下来看日志,而不是继续点。
第二个坑:清缓存不一定解决问题。网上流传的各种“清缓存大法”,对“卡在安装中”这个具体问题命中率不高,因为它的根因往往不在缓存,而在句柄、权限、网络。盲目清理反而会丢配置。
第三个坑:同一个现象,两台机器原因可能完全不同。我遇到过一台是杀软拦写入,另一台是企业内网出口策略导致下载断流,界面表现一模一样。所以那张决策表比任何“万能命令”都值钱。
第四个坑:扩展自身的兼容声明。有些版本更新后会要求更高的编辑器版本,如果编辑器版本不满足,扩展装完后会被直接禁用。这种情况表现出来很像“安装失败”,实际是在加载阶段被拦下了。更新前顺手看一眼扩展详情页的兼容说明,能省一次返工。
最后分享一个小技巧:给扩展目录做一个“更新前快照”。把当前能用的版本目录复制一份到别处,命名带日期。真出问题时,把目录拷回去、删掉.obsolete、重启,一分钟恢复原状。这招在赶进度的时候特别管用,比研究日志快多了。