數位天堂

Nokia:科技始終來自於人性; 拜耳:如果文明不能使我們更相愛,那科技便失去意義!
歡迎您的加入,讓我們一起討論科技與環保的整合應用...

您尚未登入。

#1 今天 17:15:04

Service
天使
註冊日期: 2007-07-15
文章數: 352
目前積分 :   0 

AI 兩大名醫聯手救電腦|卡在 98% 的真相

我們有一台 Windows 11 筆電,從九月底開始,每次累積更新都在重新開機後卡在 88%~98%,接著出現「正在復原變更」,一切回到原點。關機選單裡也沒有「關機」,只剩「更新並關機」和「更新並重新啟動」,不更新不能關機,更新又裝不起來。

這次除錯我們同時找了兩個 AI 助手:Claude 和 Codex。兩邊都提出過診斷,也都講錯過。後來我們讓兩邊在同一份筆記上互相檢查,才在 11 月 6 日把問題找出來:系統裡一個應用程式資料庫,83,042 筆記錄中有 1 筆損毀,擋住了更新。

這篇記錄整個過程,包括走過的彎路、用到的指令,以及這個結論的界線在哪裡。

https://digiland.tw/uploads/3_20261106_235749_ai-doctors-winupdate.jpg

■ 先講結論

  • 兇手是 StateRepository 的一筆損毀記錄:Deployment.srd 的 File 資料表有 83,042 筆,只有 1 筆讀不出來。它屬於一個舊版 Microsoft Teams(個人版)25331。
  • 這個 Teams 沒有任何使用者在用:盤點時只有「預先佈建」(給未來的新帳戶用)和系統帳戶的 Staged 狀態,沒有任何使用者註冊。
  • 修復只有一行指令:移除這個版本的預先佈建,約 5 秒,不需要重新開機。之後資料庫完整性檢查變回 ok,原本失敗的更新 KB5127936 順利安裝。
  • DISM、SFC、修復重新安裝都抓不到它:DISM 回報「還原操作已順利完成」,SFC 說「找不到任何完整性違規」,修復重新安裝後同樣失敗。
  • 界線:結論是依時間序列和模擬推出來的,結果和預測吻合,但沒有微軟官方確認;那筆記錄為什麼會壞,我們不知道。


  • ---

    ◆ 01 症狀與環境
    項目內容
    系統Windows 11 家用版 25H2(26200.8457)
    症狀累積更新重新開機後卡在 88%~98%,接著回滾
    Windows Update 錯誤碼0x800f0922(9 月、11/4)、0x800f0920(11/6)
    CBS 記錄的錯誤AppX Registration Installer,HRESULT 80670015
    試過但沒用DISM RestoreHealth、SFC、修復重新安裝、官方 ISO、安裝小幫手
    附帶狀況曾經藍屏 MEMORY_MANAGEMENT;C 槽有 BitLocker 加密;WinRE 停用
    https://www.digiland.tw/uploads/3_20261106_235235_02_shutdown_menu.png

    ---

    ◆ 02 走過的彎路

    1. 記憶體
    這台筆電藍屏過,錯誤是 MEMORY_MANAGEMENT。Windows 記憶體診斷的結果是:兩條記憶體都裝著時報錯,只留主機板上的 8 GB 時通過。Claude 當時判斷是後來加裝的 16 GB 有問題。

    拔掉 16 GB 再更新,照樣失敗(0x800f0922)。記憶體可能真的不穩,但不能證明它就是更新失敗的原因。這條 16 GB 目前仍未裝回。
    https://www.digiland.tw/uploads/3_20261106_235235_03_memory_retract.png

    2. 修復重新安裝
    9 月 30 日我們做了修復重新安裝(保留檔案和應用程式),在 99% 停了很久才裝完。裝完 Windows Update 顯示「您現在為最新狀態」,但一按「檢查更新」,跳出來的累積更新和 .NET 更新又一起失敗,錯誤碼還是 0x800f0922。

    這時 CBS 記錄已經指出失敗點是 AppX Registration Installer(錯誤 80670015),但 AppX 的服務、事件記錄、Get-AppxPackage -AllUsers 看起來都正常,線索就斷在這裡。
    https://www.digiland.tw/uploads/3_20261106_235235_04_repair_fail.png

    3. 官方 ISO 與安裝小幫手
    想用就地升級的方式重鋪一次,結果安裝程式在相容性檢查階段就擋下來(HostIsNewer → HardBlock,錯誤 0xC1900205-0x4000B),只剩「全新安裝」可選。為什麼被擋,到現在還不知道;這次沒有處理。

    4. 「磁碟 2 讀寫錯誤」
    事件記錄裡有磁碟 2 的讀寫錯誤,一度懷疑是硬碟。查了才知道磁碟 2 是一支約 4 GB 的 USB 隨身碟,和這件事無關。

    ---

    ◆ 03 關鍵線索:失敗那一秒的事件記錄

    CBS 記錄只告訴我們「AppX 註冊失敗」,沒說為什麼。真正的線索在另一份事件記錄:StateRepository。StateRepository 是 Windows 記錄「每個應用程式裝在哪、有哪些檔案」的資料庫服務,AppX 註冊時要寫入它。

    11 月 4 日那次失敗,CBS 記錄的錯誤時間是 15:41:28:

    2026-11-04 15:41:28, Error CSI ... appxreg.cpp(218): Error 80670015 ... originated in function ...AppxRegistrationInstaller::CommitChanges expression: RDSAppXOnlineNotifyPackageChanges(APPCHANGETYPE_INBOX) ... Failure will not be ignored: A rollback will be initiated ...


    同一秒,StateRepository 的事件記錄寫著:

    database disk image is malformed


    https://www.digiland.tw/uploads/3_20261106_235236_05_same_second.png
    11 月 6 日早上 07:08 又失敗一次,事件記錄更完整:

    Id 227:錯誤 0x87AF000B: StateRepository 服務無法啟動 [檢查點=800] Id 107:CriticalError 0xB: statement aborts at 13: database disk image is malformed; [INSERT INTO main.File (_FileID, _Revision, ...) SELECT s._FileID, ... FROM Source.File AS s;] Id 107:CriticalError 0xB: database corruption at line 98572 of [737ae4a347]


    查詢指令(系統管理員 PowerShell,唯讀):

    Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-StateRepository/Operational' Id = 107, 227 StartTime = (Get-Date).AddDays(-3) } | Format-List TimeCreated, Id, Message


    CBS 記錄在 C:\Windows\Logs\CBS\CBS.log,可以用 findstr 80670015 找失敗那一行,再拿它的時間去對 StateRepository 的事件。

    如果你的更新也是 0x800f0922 加上 AppX Registration Installer 80670015,建議先查這份事件記錄。

    ---

    ◆ 04 讓兩個 AI 共用一份筆記

    前面幾輪,我們是把一邊的結論複製給另一邊看,再把回覆貼回來。傳了幾輪之後,我們乾脆建了一個資料夾,放一份 notes.md,讓兩邊直接在上面討論,規則是:

  • 只往下寫,不改別人的段落。
  • 每一句主張都附上證據來源(指令輸出、記錄檔的行),沒有證據就標明「推測」。
  • 要更正對方,就引用原句,寫在自己的段落裡。

  • 效果很明顯。Claude 講錯並撤回的有好幾條,例如:把 HostIsNewer 比對的版本號說成「系統殘留的較新元件」(Codex 指出那是安裝程式自己的版本)、低估了用 SQLite .recover 搶救後覆蓋正式資料庫的風險、把失敗時間點說成「搬資料時」(Codex 用 CBS 記錄指出是更新收尾、註冊應用程式時)。Codex 則負責把每個修復提案的範圍收窄,下一節的操作條件大多是它加的。

    兩邊最後的共識是:先不修,先拿到一份資料庫複本,在複本上找出到底哪裡壞了。
    https://www.digiland.tw/uploads/3_20261106_235236_06_whiteboard.png

    ---

    ◆ 05 複製資料庫:WinRE 被 BitLocker 擋住,改用陰影複製

    資料庫在 C:\ProgramData\Microsoft\Windows\AppRepository,服務執行中檔案是鎖住的。原本打算進 WinRE(修復環境)離線複製,結果進階選項裡只有「啟動設定」和「UEFI 韌體設定」,沒有命令提示字元。reagentc /info 顯示 WinRE 是 Disabled,執行 reagentc /enable 則回答:

    Windows RE 無法在已啟用 BitLocker 磁碟機加密的磁碟區上啟用


    https://www.digiland.tw/uploads/3_20261106_235236_07_door_locked.png
    我們不想為了複製檔案去動分割區或解除加密,於是改用磁碟區陰影複製(VSS):替 C 槽拍一張快照,從快照裡把整個資料夾複製出來,用完就刪掉快照。整個過程 5 秒,1,703 個檔案、114.84 MB,失敗 0 個。
    https://www.digiland.tw/uploads/3_20261106_235236_08_snapshot.png

    腳本重點如下(系統管理員 PowerShell;目的資料夾請自行修改):

    $r = Invoke-CimMethod -ClassName Win32_ShadowCopy -MethodName Create ` -Arguments @{ Volume = 'C:\'; Context = 'ClientAccessible' } $s = Get-CimInstance Win32_ShadowCopy | Where-Object { $_.ID -eq $r.ShadowID } cmd /c mklink /d C:\ShadowAppRepo_tmp "$($s.DeviceObject)\" robocopy C:\ShadowAppRepo_tmp\ProgramData\Microsoft\Windows\AppRepository ` D:\Backup\AppRepo_original /E /B /COPY:DAT /DCOPY:T /R:0 /W:0 /NP /LOG:D:\Backup\robocopy.log cmd /c rmdir C:\ShadowAppRepo_tmp # 只移除連結 $s | Remove-CimInstance # 刪除這次建立的快照


    注意:

  • robocopy 結束碼 0~7 是成功,8 以上表示有檔案失敗;也要看記錄檔裡的「略過」「不符」「失敗」三欄。
  • -wal、-shm 檔案要一起複製,少了它們,複本內容不完整。
  • 快照是「當下瞬間」的狀態,不等於關機後的乾淨複製;這份複本只拿來診斷,不拿來還原。
  • 原始備份複製完就不再動,分析一律在另一份工作複本上做。


  • ---

    ◆ 06 在複本上逐表、逐筆找壞掉的地方

    AppRepository 裡有兩個主要資料庫:
  • StateRepository-Machine.srd:完整性檢查 ok
  • StateRepository-Deployment.srd:database disk image is malformed

  • Deployment.srd 有 30 個資料表和索引,只有一張「File」資料表讀不出來。File 有 83,042 筆,逐筆讀過一次,只有 1 筆損毀:
    https://www.digiland.tw/uploads/3_20261106_235236_09_one_bad_row.png
    欄位內容
    _FileID1309873
    Package7406(對照 Machine.srd:MicrosoftTeams_25331.1101.4152.8047_x64__8wekyb3d8bbwe)
    RelativeFilePathdesktop-assets\hashed-assets\207209-670a38f0b67525ab.js.gz
    狀況讀 _Dictionary 欄位時回報 malformed;BlockSize 讀出 NULL(定義是 NOT NULL)
    用來掃描的 Python 腳本(只在複本上執行;Python 內建的 sqlite3 就夠):

    import sqlite3, sys db = sqlite3.connect(sys.argv[1]) # 傳入「複本」的 StateRepository-Deployment.srd print("integrity_check:", db.execute("PRAGMA integrity_check").fetchall()[:3]) for (t,) in db.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall(): try: print("OK ", t, sum(1 for _ in db.execute(f'SELECT * FROM "{t}"'))) except sqlite3.DatabaseError as e: print("FAIL", t, e) rowids = [r for (r,) in db.execute("SELECT rowid FROM File")] for rid in rowids: try: db.execute("SELECT * FROM File WHERE rowid=?", (rid,)).fetchall() except sqlite3.DatabaseError as e: print(rid, e, db.execute("SELECT Package, RelativeFilePath FROM File WHERE rowid=?", (rid,)).fetchall()) print("File rows:", len(rowids))


    整張表讀不完時,只讀 rowid 通常還走得過去;再一筆一筆讀整列,就能把壞的那筆找出來。

    同一個資料夾裡還有一個 512 bytes 的 StateRepository-Deployment.srd.upgrade,修改時間正好是 11/6 07:08,和那次失敗同時。我們的推測是:服務啟動時要把資料庫升級成新的結構,以 .upgrade 為新檔,把舊資料「INSERT INTO main.File … FROM Source.File」整批搬過去,搬到這一筆就失敗,整個更新跟著回滾。這可以解釋事件記錄裡那句 SQL,但只是推測,沒有直接證據。

    我們在拋棄式複本上模擬了這次搬移:

    情境完整性檢查模擬搬移
    原狀malformed失敗(和 Windows 的錯誤相同)
    只把壞欄位設成 NULLNULL value in File.BlockSize失敗(NOT NULL 限制)
    刪掉壞的那 1 筆ok成功(83,041 筆)
    刪掉這個 Teams 的全部 6,917 筆ok成功(76,125 筆)
    模擬用的 SQLite 版本和 Windows 內建的不同,也沒有跑 StateRepository 服務本身的邏輯,所以只能當參考。
    https://www.digiland.tw/uploads/3_20261106_235236_10_simulation.png

    ---

    ◆ 07 最小的修復:不改資料庫,讓 Windows 自己移除

    直接用 SQLite 改正式資料庫(刪掉那一筆)是最快的,但我們沒有這樣做:資料庫之間、登錄和套件狀態之間的關聯我們看不到,改壞了更難收拾。比較穩的做法是用 Windows 正規的方式移除這個 Teams,讓服務自己清掉相關記錄。

    動手前 Codex 加了幾個條件:
  • 只針對完整版本名稱 MicrosoftTeams_25331.1101.4152.8047_x64__8wekyb3d8bbwe,不要用「名稱含 Teams 就全刪」,也不要動其他版本的 Teams。
  • 先盤點:目前使用者、所有使用者、預先佈建三份清單都要看。
  • 只做一次,失敗就停,不重試、不強制。
  • 做完先不要更新,再拍一次快照確認資料庫真的好了。

  • 盤點結果:

    Get-AppxPackage -AllUsers -Name MicrosoftTeams PackageFullName : MicrosoftTeams_25331.1101.4152.8047_x64__8wekyb3d8bbwe PackageUserInformation : {S-1-5-18 [S-1-5-18]: Staged} Get-AppxProvisionedPackage -Online | Where-Object DisplayName -eq MicrosoftTeams PackageName : MicrosoftTeams_25331.1101.4152.8047_x64__8wekyb3d8bbwe


    S-1-5-18 是系統帳戶,狀態是 Staged(已放好檔案、還沒註冊給任何人);另外它在預先佈建清單裡,準備給之後新建的帳戶用。也就是說,沒有任何使用者註冊這個版本的 Teams。
    https://www.digiland.tw/uploads/3_20261106_235236_11_zero_users.png

    於是只執行一行:

    Remove-AppxProvisionedPackage -Online -PackageName 'MicrosoftTeams_25331.1101.4152.8047_x64__8wekyb3d8bbwe'


    約 5 秒完成,回傳 RestartNeeded: False。再盤點一次,兩份清單裡都沒有它了(連 Staged 都消失),所以原本準備的第二步 Remove-AppxPackage -AllUsers 就沒有執行。

    第二張快照的檢查結果:

  • Deployment.srd 完整性檢查:ok(移除前是 malformed)
  • File 資料表剩 76,125 筆,和事前「刪掉這個 Teams 全部記錄」的模擬數字一模一樣
  • _FileID 1309873 和那個 .js.gz 路徑都已不存在
  • 在新複本上模擬搬移:成功

  • https://www.digiland.tw/uploads/3_20261106_235236_12_76125.png

    ---

    ◆ 08 更新成功

    重試原本失敗的更新:2026-10 預覽更新 KB5127936(26200.9818)「已順利在 2026/11/6 安裝」。Windows Update 顯示「您現在為最新狀態」,之前一直被鎖住的「暫停更新」按鈕也恢復可以按了。
    https://www.digiland.tw/uploads/3_20261106_235236_13_update_ok.png

    最後用 ◆03 的指令查 13:00 之後的 StateRepository 事件 107/227:沒有任何輸出。

    ---

    ■ 這次學到的五件事
    https://www.digiland.tw/uploads/3_20261106_235237_14_five_lessons.png
  • 1. 檢查報錯,不等於找到原因:要看拿掉它之後,問題有沒有消失。記憶體檢查報錯了,但拔掉 16 GB 之後更新照樣失敗。
  • 2. 系統檢查都說沒事,不代表真的沒事:把不同記錄裡同一秒發生的事對起來,線索才接得上。DISM、SFC 都說沒問題;CBS 和 StateRepository 在 15:41:28 對上,才找到資料庫。
  • 3. 意見不同時,放上同一塊白板:與其靠我們在兩邊之間傳話,不如讓兩邊在同一份筆記上討論;每句話附證據,講錯就撤回。
  • 4. 正式的資料庫先不要動:從快照取得副本,在副本上找線索。只是快照是開機中的瞬間狀態,不等於關機後的完整複製。
  • 5. 動手之前先在複本上模擬,做完再量一次:事前算出移除後應該剩 76,125 筆,做完實際量到 76,125 筆,數字對得上才算確認。


  • ---

    ■ 這次沒有解決、也不能下結論的事
  • 這個結論是用時間序列和模擬推出來的,和實際結果吻合,但沒有微軟官方確認。
  • 那筆記錄為什麼會壞,不知道。不能說是記憶體造成的;那條 16 GB 在確認沒問題之前先不裝回。
  • 更早 9 月卡在 88% 那幾次,當時沒有查這個資料庫,所以不能說每一次都是同一筆記錄造成的。
  • HostIsNewer 為什麼擋住 ISO 升級,原因未明。
  • WinRE 仍是停用狀態,之後另外處理。
  • 壞的是資料庫裡的一筆記錄,不是 Teams 這個軟體本身有問題。

  • https://www.digiland.tw/uploads/3_20261106_235237_15_discharge.png

    ■ 如果你也遇到類似狀況
  • 更新失敗且 CBS 記錄裡有 AppX Registration Installer 80670015,先查 StateRepository 事件記錄有沒有「database disk image is malformed」。
  • 有的話,先用陰影複製把 AppRepository 整個資料夾(含 -wal、-shm)複製出來,在複本上跑完整性檢查,找出是哪個資料庫、哪一筆、屬於哪個套件。
  • 不要直接改正式資料庫,也不要拿 .recover 的結果覆蓋回去;優先用 Windows 正規方式移除那個套件,範圍越小越好,移除前先盤點。
  • 修完先再拍一次快照確認,再重試更新;失敗就停下來看記錄,不要一直重試。


  • ---

    ■ 參考連結
  • Remove-AppxProvisionedPackage(Microsoft Learn):https://learn.microsoft.com/powershell/ … nedpackage
  • Remove-AppxPackage(Microsoft Learn):https://learn.microsoft.com/powershell/ … ppxpackage
  • Win32_ShadowCopy 的 Create 方法(Microsoft Learn):https://learn.microsoft.com/previous-ve … shadowcopy
  • SQLite PRAGMA integrity_check:https://www.sqlite.org/pragma.html#prag … rity_check


  • (記錄、指令輸出與數字都是我們自己這台筆電的實測,歡迎討論或指正。)



    Intel Arc Pro B50

    線上

     

    友情連結

    論壇頁尾

    Powered by PunBB
    © Copyright 2018 Rickard Andersson
    RSS Feed