scoop更新tailscale失败(MSI安装全面失效)的排查与修复

故障表现

  • scoop update tailscale:旧版 1.98.8 卸载成功,接着解包新版 MSI 时报错 ERROR Exit code was 1603!
  • 后果:Tailscale 被卸干净、新版没装上、系统服务一并消失,VPN 直接断掉
  • 事件日志:MsiInstaller 事件 10005,error code is 2203

关键判断:不是 Tailscale 的问题

统计近 180 天的 MSI 记录 —— 失败 245 次,成功 0 次,且横跨完全无关的厂商。说明这是机器级的 Windows Installer 故障:任何走 MSI 的软件都装不上、更新不了、也修复不了。

产品失败次数
Tailscale(自带更新器,约每 3.7 小时重试一次)220
AMD 显卡驱动系列(AMD Settings / DVR / WVR64 / Branding64 / RyzenMasterSDK)各 4
Google Update Helper1
Episteme oss1

最早可见失败:2026-09-14 10:28(受日志保留上限,实际起点可能更早)。另外注意:2026-09-27 那次 AMD 显卡驱动更新其实也是失败的,当时可能被误以为装上了。

定位到具体失败点

用 msiexec /a(管理式安装,只解包、非破坏性)复现并抓详细日志:

Action start  : InstallInitialize.
DEBUG: Error 2203: Database: C:\windows\Installer\inprogressinstallinfo.ipi.
       Cannot open database file. System error -2147286788
Action ended : InstallInitialize. Return value 3.

msiexec 每次安装前的第一个动作,是在 C:\Windows\Installer\ 下创建一个 inprogressinstallinfo.ipi(「本次安装正在进行中」标记文件)。它创建不出来,于是在 InstallInitialize 直接判定失败(返回值 3),整个安装回滚为 1603。

把错误码翻译成人话:

HRESULT含义出现阶段
0x80030002STG_E_FILENOTFOUND先尝试打开已存在的 .ipi(不存在,正常)
0x800300FCSTG_E_INVALIDNAME再尝试创建它 → 失败

STG_E_INVALIDNAME 表示底层 CreateFile 返回 ERROR_INVALID_NAME(123) ——「文件名、目录名或卷标语法不正确」。即:一个完全合法的路径,被内核判成了「名字语法非法」。权限不足只会返回 ACCESS_DENIED,所以方向从一开始就不该往权限、杀软上找。

逐层排除(全部实测,非推测)

排查项实测结果判定
MSI 核心文件是否被篡改msiexec.exe / msi.dll / msihnd.dll / msimsg.dll / msisip.dll 全部微软签名 Valid,版本与 3-29 补丁一致清白
目录权限与所有者C:\Windows\Installer:Everyone 读取执行、SYSTEM 完全控制、Administrators 完全控制;所有者 BUILTIN\Administrators;无 Deny正常
目录属性Hidden + System + Directory(raw 0x16),未设只读,非重解析点,未开启区分大小写正常
残留状态C:\Config.Msi 不存在、Installer\InProgress 键不存在、inprogressinstallinfo.ipi 不存在无残留
第三方微过滤器fltmc filters 只有微软自带 10 个无
第三方传统过滤驱动卷类 UpperFilters 里只有 AOMEI 的 ambakdrv,但它 2025-03-23 就装了,比故障早 18 个月排除
杀软Defender 未运行,SecurityCenter2 无第三方杀软无拦截者
文件系统chkdsk C: /scan 三个阶段全跑完(文件记录/索引/安全描述符/USN),坏扇区 0;仅 1 个 AMD 缓存孤立文件健康
注入点AppInit_DLLs 为空、LoadAppInit_DLLs=0、GlobalFlag=0、无 VerifierDlls、无 AppCompat 垫片、msiexec 无 IFEO 条目干净
msiserver 服务配置ObjectName=LocalSystem、Type=16、Start=3、ImagePath 正确正常
服务令牌实际权限直接读进程令牌:16 项,SeTcb / SeBackup / SeRestore / SeTakeOwnership / SeChangeNotify / SeSecurity 全在且已启用未被削权
SYSTEM profile目录树完整,各级 ACL 正确(SYSTEM + Administrators 完全控制)正常
路径拦截策略无 SRP 路径规则、无 AppLocker、无 Installer 策略组无
Windows 更新最后一次 2026-03-29,故障窗口内零更新与补丁无关
重新注册引擎msiexec /unregister + /regserver 后依然 1603无效
sfc /scannow只修了 1 个无关文件(USOPrivate 里 Outlook 更新器的 json),MSI 依旧失败无关

手动复现实验(关键转折)

为了分清「文件系统层」还是「MSI 引擎层」,用管理员身份做了几组对照:

实验动作结果
A普通 CreateFile 在 C:\Windows\Installer 建一个探针文件成功
B直接 P/Invoke 调用 msiexec 用的同一个 API StgCreateStorageEx,建精确文件名 inprogressinstallinfo.ipi成功
C换名字、换扩展名、换目录(C:\ 根、%TEMP%、相似名矩阵)全部成功

接着又以 LocalSystem 身份(用一次性 SYSTEM 计划任务)跑 B:依然成功。

也就是说:文件、文件名、目录、ACL、权限、OLE 层、API、进程身份 —— 全部被证明可用,但 msiexec 就是不行。这把范围逼到了「唯一差异是 msiexec 这个进程读到的东西」。

根本原因

系统环境变量 TEMP / TMP 被改坏成畸形的:

HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
  TEMP   [ExpandString] = [%C:\windows%\TEMP]     <== 畸形
  TMP    [ExpandString] = [%C:\windows%\TEMP]     <== 畸形
  windir [ExpandString] = [%SystemRoot%]           <== 这是 Windows 正常默认值

正常应为 %SystemRoot%\TEMP。这里 %...% 里包的是一个字面路径而不是变量名(某个「系统优化/加固」脚本把 %SystemRoot% 替换成了 %C:\windows%)。

展开机制:ExpandEnvironmentStrings 遇到不认识的变量名会原样保留,于是展开后得到的还是字面量 %C:\windows%\TEMP。这个字符串当路径使用时,第一段是 %C: —— 中间含冒号,属于非法路径段 → 内核返回 ERROR_INVALID_NAME(123) → OLE 报 STG_E_INVALIDNAME → MSI 在 InstallInitialize 直接死 → 2203 → 1603。

为什么这个 Bug 极难定位

因为它只砸服务、不砸你手动开的程序:

调用者TEMP 来源结果误导性
交互式管理员HKCU → C:\Users\cat\AppData\Local\Temp完全正常让人误判「环境没问题」
手动调同一个 OLE API(绝对路径)不碰 TEMP成功让人误判「OLE 没问题」
SYSTEM 计划任务(绝对路径)不碰 TEMP成功让人误判「SYSTEM 身份也没问题」
msiexec(LocalSystem 服务)HKLM 系统环境失败只有它同时满足「跑在 SYSTEM」+「必须用 TEMP」

最终靠「以 SYSTEM 身份打印环境变量」才看到真相:

IDENTITY = NT AUTHORITY\SYSTEM
SessionId = 0
TEMP env = %C:\windows%\TEMP      <== 铁证

普通管理员 shell 读不到 SYSTEM 的环境,必须用一次性 SYSTEM 计划任务去读。

修复

----------- 先备份 -----------
reg export "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" env-backup.reg /y

----------- 改回 Windows 默认值 -----------
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v TEMP /t REG_EXPAND_SZ /d "%%SystemRoot%%\TEMP" /f
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v TMP  /t REG_EXPAND_SZ /d "%%SystemRoot%%\TEMP" /f

----------- windir 保持 %SystemRoot% 不动(那本来就是默认值)-----------

----------- 让 msiserver 重新读环境(无需重启即可复测)-----------
Restart-Service msiserver -Force

说明:reg add 里 %% 是 cmd 下对 % 的转义,写进注册表的实际值是 %SystemRoot%\TEMP。改完我重启了 msiserver,所以它立刻生效;其余早已在运行的服务要到下次重启才会拿到新值。

验证

修复前:msiexec 退出码 1603 / InstallInitialize Return value 3
修复后:msiexec 退出码 0    / InstallInitialize Return value 1

Windows Installer 事件日志(独立取证):
  12:13  1040  Beginning a Windows Installer transaction: ...tailscale-setup-1.102.4-amd64.msi
  12:14 11707  Product: Tailscale -- Installation completed successfully.
  12:14  1033  ... Installation success or error status: 0
  12:14  1042  Ending a Windows Installer transaction

这是这台机器至少 180 天以来第一次成功的 MSI 操作。

Tailscale 的恢复过程

  • 先用 7-Zip 直接从哈希校验通过的官方 MSI 里解出 1.102.4 原版二进制(绕过坏掉的 MSI),按 MSI 组件映射还原文件名:ClientCmd→tailscale.exe、ClientGUIx64→tailscale-ipn.exe、Service→tailscaled.exe、WinTunApiLib→wintun.dll;再 scoop reset tailscale@1.102.4 重建 current 链接与 shims
  • MSI 修好后补上系统服务:tailscaled.exe install-system-daemon
  • 结果:服务 Running / 自动,版本 1.102.4,隧道 IP 100.64.154.28,对端节点可见 —— 状态文件还在,无需重新登录

附:Tailscale 改为手动模式

需求:平时不占后台,需要时手动点开。实测两条路都可行(都不需要管理员、不弹 UAC,因为 Tailscale 安装时给服务加了允许普通用户启动的权限):

方式做法服务
手动启服务(已采用)双击桌面 Tailscale 图标 → 服务自动启动并连上用到才起
只开关连接双击桌面 Tailscale-断开(即 tailscale down),或托盘右键 Disconnect;再点图标是秒连一直运行
---------- 设为手动模式 ----------
sc config Tailscale start= demand

---------- 改回开机自动 ----------
sc config Tailscale start= auto

---------- 常用命令 ----------
tailscale status
tailscale down
tailscale up

风险提示:手动模式下重启后 Tailscale 是断的,直到手动点一次。若有「人在外网要连回这台机器」或「定时任务依赖本机在 tailnet 上」的需求,会因此失效。

关联

同一族故障在 2026-07-23 已经发作过一次 —— 当时是删掉 tmp 变量导致 cmd / powershell 全线故障,修复记录见 终端环境故障。两次都指向同一个薄弱点:系统环境变量被「优化」操作改坏,但表现形式完全不同(那次是终端打不开,这次是 MSI 全灭)。那篇笔记里已经写过系统 TEMP/TMP 的正确写法,本次是它被再次改坏。

遗留与建议

  • 系统环境变量已全盘扫描(%盘符:\ 畸形模式),除 TEMP/TMP 外无其他残留
  • 建议排查那个把 %SystemRoot% 改成 %C:\windows% 的「优化/加固」工具;同一工具还把 SeAssignPrimaryTokenPrivilege 多余授予了用户 cat
  • 建议重启一次:让所有仍在运行的服务拿到修好后的环境变量,并顺带让 chkdsk C: /spotfix 修复 AMD 缓存里的孤立文件
  • 备份文件:env-backup.reg(记录改坏前的原始值)