故障表现
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 Helper | 1 |
| Episteme oss | 1 |
最早可见失败: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 | 含义 | 出现阶段 |
| 0x80030002 | STG_E_FILENOTFOUND | 先尝试打开已存在的 .ipi(不存在,正常) |
| 0x800300FC | STG_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(记录改坏前的原始值)