Epic Games Launcher stuck on Resume after UE 5.8.x update – manifest commit never completes [Workaround v2]

Update – September 2026

This issue has now been reproduced on both UE 5.8.1 → 5.8.2 and UE 5.8.2 → 5.8.3.

Epic has opened UE-398557, currently marked Ready for QA.

I also added a generic PowerShell diagnostic/workaround v2.0 below. It automatically detects the affected UE 5.8.x target version from the Pending transaction and runs in read-only DRY-RUN mode by default.

Use v2.0 instead of the older 5.8.2-specific workaround.

Summary

Epic Games Launcher gets stuck in a Resume loop after updating Unreal Engine 5.8.1 to 5.8.2.

The engine files are successfully updated to UE 5.8.2 / CL 56702186, and BuildPatch reports ProcessSuccess=TRUE / ErrorCode=OK. However, the Launcher fails to commit the final installed manifest and returns to Resume without downloading anything.

The Launcher log shows:
IncompleteInstall=1 AutoResume=1
followed by:
completed, waiting for manifest commit before starting next download

There is also a suspicious MainGameAppId mismatch where the valid on-disk AppId is reconciled against catalog=(null):(null):(null).

What Type of Bug are you experiencing?

Other

Steps to Reproduce

  1. Have Unreal Engine 5.8.1 installed through Epic Games Launcher.
  2. Start the update to Unreal Engine 5.8.2.
  3. Allow the update to finish.
  4. Observe that Epic Games Launcher changes the engine state to “Resume”.
  5. Click Resume/Update again.
  6. The operation runs for only a few seconds, downloads no additional engine data, and returns to “Resume”.
  7. Check UnrealEditor.exe properties or Engine/Build/Build.version:
    • File version: 5.8.2.0
    • Product version: ++UE5+Release-5.8-CL-56702186
  8. Check the Launcher log. BuildPatch reports success, but the installation remains marked as incomplete and waits for a manifest commit.

Expected Result

After BuildPatch successfully completes the UE 5.8.2 update, Epic Games Launcher should commit the new installed manifest, clear the incomplete-install state, and display Unreal Engine 5.8.2 with the Launch button.

Observed Result

The Unreal Engine files are already UE 5.8.2 / CL 56702186, but Epic Games Launcher continues to report the previous 5.8.1 installation state and shows Resume.

Repeated Resume/Update attempts do not download additional data.

The log reports a successful BuildPatch operation, followed by:

IncompleteInstall=1 AutoResume=1

and:

completed, waiting for manifest commit before starting next download

The manifest commit does not complete.

A related warning appears immediately before this:

MainGameAppId mismatch detected for app UE_5.8
on-disk=ue:18b3b415bc434c5b974f50488360ca31:UE_5.8
catalog=(null):(null):(null)
Correcting to catalog value.

Affects Versions

5.8

Platform(s)

Windows

Upload an image

!

Additional Notes

Additional diagnostics:

The physical Unreal Engine installation was already fully updated to UE 5.8.2 / CL 56702186 before the Launcher UI was fixed.

The main UE 5.8 manifest matched the 5.8.2 build, and BuildPatch completed with:
ProcessSuccess: TRUE
ErrorCode: OK
FailureReasonText: The operation was successful.

The issue was isolated to the Launcher metadata/final manifest commit stage.

For diagnostic purposes, after backing up the original metadata and with Epic Games Launcher completely closed, I manually reconciled the installed UE_5.8 metadata to the already-installed 5.8.2 manifest and removed only the stale UE 5.8 pending transaction files.

After restarting Epic Games Launcher, it immediately displayed:
Unreal Engine 5.8.2
Launch

No additional Engine files had to be downloaded.

I have preserved the original installed .item, pending .item, manifests and Launcher logs if Epic needs them for investigation.

1 Like

Update: confirmed workaround + safe PowerShell diagnostic

Another user has now reported what appears to be the same UE 5.8.1 → 5.8.2 Launcher issue, so I am adding the diagnostic/workaround I used to recover my installation.

Important clarification

In my case, the very first UE 5.8.1 → 5.8.2 Update attempt had already updated the actual Engine files successfully.

After that first Update:

UnrealEditor.exe
Numeric FileVersion: 5.8.2.0
ProductVersion: ++UE5+Release-5.8-CL-56702186

Engine\Build\Build.version
Version: 5.8.2
Changelist: 56702186

However, Epic Games Launcher still displayed:

5.8.1
Resume

Clicking Resume/Update again downloaded no meaningful Engine data and returned to Resume.

The Launcher log showed that BuildPatch itself completed successfully:

ProcessSuccess: TRUE
ErrorCode: OK
FailureReasonText: The operation was successful.

but afterwards remained at:

IncompleteInstall=1 AutoResume=1
completed, waiting for manifest commit before starting next download

Therefore this workaround is not intended to force-install UE 5.8.2. It only attempts to reconcile the Launcher metadata when the actual Engine has already been successfully updated to 5.8.2 / CL 56702186.


PowerShell diagnostic / workaround

The script is deliberately READ-ONLY by default:

$ApplyChanges = $false

In this mode it:

  • locates the installed UE_5.8 Launcher metadata automatically,
  • verifies UnrealEditor.exe,
  • verifies Engine\Build\Build.version,
  • verifies the installed manifest,
  • checks LauncherInstalled.dat,
  • detects the active UE 5.8 pending transaction,
  • validates the pending UE 5.8.2 metadata,
  • simulates the final metadata commit in memory only.

It does not modify, copy or delete anything.

If the system is already fixed, the script exits with:

STATUS: ALREADY HEALTHY
UE 5.8.2 metadata is already committed.
No active UE 5.8 Pending transaction exists.
No changes were made.

I tested this state after recovering my installation and the script correctly detected the healthy UE 5.8.2 installation.

If the problem is present, the expected diagnostic result is:

STATUS: AFFECTED / CONDITIONS MATCH

Physical Engine:
  UE 5.8.2 / CL 56702186

Installed metadata:
  5.8.1-56057345+++UE5+Release-5.8-Windows

Pending target:
  5.8.2-56702186+++UE5+Release-5.8-Windows

In-memory commit simulation:
  PASSED

DRY RUN PASSED
No files were modified, copied or deleted.

Only after DRY RUN PASSED, the same script can be changed to:

$ApplyChanges = $true

The Apply mode creates a complete backup before changing anything, then asks the user to completely exit Epic Games Launcher and explicitly type:

APPLY

before performing the metadata reconciliation.

The backup contains the installed .item, Pending .item, installed manifest, Pending manifest, LauncherInstalled.dat and Build.version.

The script removes only the UE 5.8-specific pending transaction and does not remove the entire Pending directory.

In my case, after the workaround and restarting Epic Games Launcher, it immediately displayed:

Unreal Engine 5.8.2
Launch

No additional Engine download was required.

Warning

This is an unofficial workaround, not an Epic fix.

Do not use Apply mode unless the DRY-RUN confirms that the physical Engine is already exactly UE 5.8.2 / CL 56702186 and the remaining problem is the Launcher metadata/pending transaction.

Keep the automatically-created backup until Epic provides an official fix.

:warning: Deprecated – use v2.0 instead

This older workaround is specific to UE 5.8.2 and has been superseded by the generic UE 5.8.x PowerShell diagnostic/workaround v2.0.

Please use v2.0 for UE 5.8.2, 5.8.3 and later 5.8.x hotfixes.

Tip: The copy button for the full script is in the top-right corner of the code block.

& {
    $ErrorActionPreference = "Stop"
    Set-StrictMode -Version Latest


    # ============================================================
    # MODE
    # ============================================================
    #
    # DEFAULT = SAFE / READ-ONLY
    #
    # Leave this as $false first.
    #
    # Change to $true ONLY after DRY RUN PASSED and only if the
    # system matches the issue described in this thread.
    #
    # ============================================================

    $ApplyChanges = $false


    # ============================================================
    # EXPECTED UE BUILD
    # ============================================================

    $ExpectedAppVersion =
        "5.8.2-56702186+++UE5+Release-5.8-Windows"

    $PreviousAppVersion =
        "5.8.1-56057345+++UE5+Release-5.8-Windows"

    $ExpectedMajor = 5
    $ExpectedMinor = 8
    $ExpectedPatch = 2
    $ExpectedCL    = 56702186


    # ============================================================
    # LAUNCHER PATHS
    # ============================================================

    $ManifestRoot =
        "C:\ProgramData\Epic\EpicGamesLauncher\Data\Manifests"

    $PendingRoot =
        Join-Path $ManifestRoot "Pending"

    $LauncherDat =
        "C:\ProgramData\Epic\UnrealEngineLauncher\LauncherInstalled.dat"


    # ============================================================
    # HELPER
    # ============================================================

    function Read-JsonFile {
        param(
            [Parameter(Mandatory = $true)]
            [string]$Path
        )

        Get-Content -LiteralPath $Path -Raw |
            ConvertFrom-Json
    }


    Write-Host ""
    Write-Host "==========================================="
    Write-Host " UE 5.8.2 LAUNCHER MANIFEST DIAGNOSTIC"
    Write-Host "==========================================="

    if ($ApplyChanges) {
        Write-Host "MODE: APPLY CHANGES"
    }
    else {
        Write-Host "MODE: DRY RUN / READ-ONLY"
    }


    # ============================================================
    # 1. FIND INSTALLED UE_5.8 ITEM
    # ============================================================

    if (-not (Test-Path $ManifestRoot)) {
        throw "STOP: Epic Games Launcher manifest directory was not found."
    }

    $MainCandidates = @(
        Get-ChildItem `
            -LiteralPath $ManifestRoot `
            -Filter "*.item" `
            -File |
        ForEach-Object {

            try {
                $J = Read-JsonFile $_.FullName

                if ($J.AppName -eq "UE_5.8") {
                    [PSCustomObject]@{
                        Path = $_.FullName
                        Data = $J
                    }
                }
            }
            catch {
                # Ignore unrelated/unreadable item files.
            }
        }
    )

    if ($MainCandidates.Count -ne 1) {
        throw "STOP: Expected exactly one installed UE_5.8 item, found $($MainCandidates.Count)."
    }

    $MainItemPath =
        $MainCandidates[0].Path

    $Main =
        $MainCandidates[0].Data

    Write-Host ""
    Write-Host "[1] Installed UE_5.8 metadata"
    Write-Host "Item       : $MainItemPath"
    Write-Host "Version    : $($Main.AppVersionString)"
    Write-Host "Incomplete : $($Main.bIsIncompleteInstall)"
    Write-Host "Location   : $($Main.InstallLocation)"


    # ============================================================
    # 2. VERIFY PHYSICAL UNREAL ENGINE
    # ============================================================

    if ([string]::IsNullOrWhiteSpace($Main.InstallLocation)) {
        throw "STOP: UE_5.8 InstallLocation is empty."
    }

    $EditorExe =
        Join-Path `
            $Main.InstallLocation `
            "Engine\Binaries\Win64\UnrealEditor.exe"

    if (-not (Test-Path $EditorExe)) {
        throw "STOP: UnrealEditor.exe was not found."
    }

    $VI =
        (Get-Item $EditorExe).VersionInfo

    $NumericFileVersion =
        "{0}.{1}.{2}.{3}" -f `
            $VI.FileMajorPart,
            $VI.FileMinorPart,
            $VI.FileBuildPart,
            $VI.FilePrivatePart

    Write-Host ""
    Write-Host "[2] Physical UnrealEditor.exe"
    Write-Host "Numeric FileVersion : $NumericFileVersion"
    Write-Host "FileVersion string  : $($VI.FileVersion)"
    Write-Host "ProductVersion      : $($VI.ProductVersion)"

    if (
        $VI.FileMajorPart -ne $ExpectedMajor -or
        $VI.FileMinorPart -ne $ExpectedMinor -or
        $VI.FileBuildPart -ne $ExpectedPatch
    ) {
        throw "STOP: UnrealEditor.exe is not UE 5.8.2."
    }

    if ($VI.ProductVersion -notlike "*CL-$ExpectedCL*") {
        throw "STOP: UnrealEditor.exe is not CL $ExpectedCL."
    }


    # ============================================================
    # 3. VERIFY Engine\Build\Build.version
    # ============================================================

    $BuildVersionPath =
        Join-Path `
            $Main.InstallLocation `
            "Engine\Build\Build.version"

    if (-not (Test-Path $BuildVersionPath)) {
        throw "STOP: Engine\Build\Build.version was not found."
    }

    $Build =
        Read-JsonFile $BuildVersionPath

    Write-Host ""
    Write-Host "[3] Engine Build.version"
    Write-Host "Version    : $($Build.MajorVersion).$($Build.MinorVersion).$($Build.PatchVersion)"
    Write-Host "Changelist : $($Build.Changelist)"

    if (
        $Build.MajorVersion -ne $ExpectedMajor -or
        $Build.MinorVersion -ne $ExpectedMinor -or
        $Build.PatchVersion -ne $ExpectedPatch -or
        $Build.Changelist -ne $ExpectedCL
    ) {
        throw "STOP: Build.version does not report UE 5.8.2 / CL $ExpectedCL."
    }


    # ============================================================
    # 4. VERIFY INSTALLED MANIFEST
    # ============================================================

    if ([string]::IsNullOrWhiteSpace($Main.CompleteManifestPath)) {
        throw "STOP: CompleteManifestPath is empty."
    }

    if (-not (Test-Path $Main.CompleteManifestPath)) {
        throw "STOP: Installed manifest does not exist: $($Main.CompleteManifestPath)"
    }

    $InstalledManifestHash =
        (Get-FileHash `
            $Main.CompleteManifestPath `
            -Algorithm SHA256).Hash

    Write-Host ""
    Write-Host "[4] Installed manifest"
    Write-Host "Path   : $($Main.CompleteManifestPath)"
    Write-Host "SHA256 : $InstalledManifestHash"


    # ============================================================
    # 5. READ LauncherInstalled.dat
    # ============================================================

    if (-not (Test-Path $LauncherDat)) {
        throw "STOP: LauncherInstalled.dat was not found."
    }

    $Launcher =
        Read-JsonFile $LauncherDat

    $UE58Entries = @(
        $Launcher.InstallationList |
        Where-Object {
            $_.AppName -eq "UE_5.8"
        }
    )

    if ($UE58Entries.Count -ne 1) {
        throw "STOP: Expected exactly one UE_5.8 entry in LauncherInstalled.dat."
    }

    $UE58 =
        $UE58Entries[0]

    Write-Host ""
    Write-Host "[5] LauncherInstalled.dat"
    Write-Host "AppName    : $($UE58.AppName)"
    Write-Host "AppVersion : $($UE58.AppVersion)"


    # ============================================================
    # 6. FIND ACTIVE UE_5.8 PENDING TRANSACTION
    # ============================================================

    $PendingCandidates = @()

    if (Test-Path $PendingRoot) {

        $PendingCandidates = @(
            Get-ChildItem `
                -LiteralPath $PendingRoot `
                -Filter "*.item" `
                -File `
                -ErrorAction SilentlyContinue |
            ForEach-Object {

                try {
                    $J = Read-JsonFile $_.FullName

                    if ($J.AppName -eq "UE_5.8") {
                        [PSCustomObject]@{
                            Path = $_.FullName
                            Data = $J
                        }
                    }
                }
                catch {
                    # Ignore unrelated/unreadable Pending items.
                }
            }
        )
    }


    # ============================================================
    # ALREADY HEALTHY?
    # ============================================================

    if ($PendingCandidates.Count -eq 0) {

        if (
            $Main.AppVersionString -eq $ExpectedAppVersion -and
            $Main.bIsIncompleteInstall -eq $false -and
            $UE58.AppVersion -eq $ExpectedAppVersion
        ) {

            Write-Host ""
            Write-Host "==========================================="
            Write-Host " STATUS: ALREADY HEALTHY"
            Write-Host "==========================================="
            Write-Host "UE 5.8.2 metadata is already committed."
            Write-Host "No active UE 5.8 Pending transaction exists."
            Write-Host "No changes were made."
            Write-Host "==========================================="

            return
        }

        Write-Host ""
        Write-Host "==========================================="
        Write-Host " STATUS: PENDING TRANSACTION NOT FOUND"
        Write-Host "==========================================="
        Write-Host ""
        Write-Host "The physical Engine is UE 5.8.2, but no active"
        Write-Host "UE_5.8 Pending transaction was found."
        Write-Host ""
        Write-Host "If Epic Games Launcher is stuck on Update/Resume:"
        Write-Host ""
        Write-Host "1. Open Epic Games Launcher."
        Write-Host "2. Click Update/Resume once."
        Write-Host "3. Wait until it returns to Resume."
        Write-Host "4. Run this script again BEFORE closing Launcher."
        Write-Host ""
        Write-Host "No changes were made."
        Write-Host "==========================================="

        return
    }


    if ($PendingCandidates.Count -ne 1) {
        throw "STOP: More than one UE_5.8 Pending item was found."
    }

    $PendingItemPath =
        $PendingCandidates[0].Path

    $Pending =
        $PendingCandidates[0].Data

    Write-Host ""
    Write-Host "[6] Active Pending transaction"
    Write-Host "Item       : $PendingItemPath"
    Write-Host "Version    : $($Pending.AppVersionString)"
    Write-Host "Incomplete : $($Pending.bIsIncompleteInstall)"
    Write-Host "PrereqSHA1 : $($Pending.PrereqSHA1Hash)"


    # ============================================================
    # 7. VALIDATE PENDING METADATA
    # ============================================================

    if ($Pending.AppVersionString -ne $ExpectedAppVersion) {
        throw "STOP: Pending transaction is not UE 5.8.2 / CL $ExpectedCL."
    }

    if ($Pending.bIsIncompleteInstall -ne $true) {
        throw "STOP: Pending UE_5.8 item is not marked as incomplete."
    }

    if (
        [string]::IsNullOrWhiteSpace($Pending.PrereqSHA1Hash) -or
        $Pending.PrereqSHA1Hash -notmatch '^[0-9A-Fa-f]{40}$'
    ) {
        throw "STOP: Pending PrereqSHA1Hash is invalid."
    }

    if (
        -not [string]::IsNullOrWhiteSpace($Pending.InstallLocation) -and
        $Pending.InstallLocation -ne $Main.InstallLocation
    ) {
        throw "STOP: Pending InstallLocation does not match installed UE_5.8."
    }


    # ============================================================
    # 8. LOCATE PENDING MANIFEST
    # ============================================================

    if ([string]::IsNullOrWhiteSpace($Main.PendingManifestPath)) {
        throw "STOP: Installed UE_5.8 item has no PendingManifestPath."
    }

    $PendingManifestPath =
        $Main.PendingManifestPath

    if (-not (Test-Path $PendingManifestPath)) {
        throw "STOP: Pending UE 5.8 manifest was not found: $PendingManifestPath"
    }

    $PendingManifestHash =
        (Get-FileHash `
            $PendingManifestPath `
            -Algorithm SHA256).Hash

    Write-Host ""
    Write-Host "[7] Pending manifest"
    Write-Host "Path   : $PendingManifestPath"
    Write-Host "SHA256 : $PendingManifestHash"


    # ============================================================
    # 9. VALIDATE CURRENT METADATA VERSION
    # ============================================================

    $AllowedInstalledVersions =
        @(
            $PreviousAppVersion,
            $ExpectedAppVersion
        )

    if ($Main.AppVersionString -notin $AllowedInstalledVersions) {
        throw "STOP: Installed metadata contains an unexpected UE_5.8 version."
    }

    if ($UE58.AppVersion -notin $AllowedInstalledVersions) {
        throw "STOP: LauncherInstalled.dat contains an unexpected UE_5.8 version."
    }


    # ============================================================
    # 10. SIMULATE THE MANIFEST COMMIT IN MEMORY
    # ============================================================

    $SimMain =
        Read-JsonFile $MainItemPath

    $SimMain.AppVersionString =
        $Pending.AppVersionString

    $SimMain.PrereqSHA1Hash =
        $Pending.PrereqSHA1Hash

    $SimMain.bIsIncompleteInstall =
        $false


    $SimLauncher =
        Read-JsonFile $LauncherDat

    $SimEntries = @(
        $SimLauncher.InstallationList |
        Where-Object {
            $_.AppName -eq "UE_5.8"
        }
    )

    if ($SimEntries.Count -ne 1) {
        throw "STOP: Simulation could not uniquely resolve UE_5.8."
    }

    $SimEntries[0].AppVersion =
        $ExpectedAppVersion


    # Serialize + parse again to verify that resulting JSON remains valid.

    $SimMainJson =
        $SimMain |
        ConvertTo-Json -Depth 20

    $SimLauncherJson =
        $SimLauncher |
        ConvertTo-Json -Depth 20

    $ParsedMain =
        $SimMainJson |
        ConvertFrom-Json

    $ParsedLauncher =
        $SimLauncherJson |
        ConvertFrom-Json

    $ParsedUE58 = @(
        $ParsedLauncher.InstallationList |
        Where-Object {
            $_.AppName -eq "UE_5.8"
        }
    )

    if ($ParsedMain.AppVersionString -ne $ExpectedAppVersion) {
        throw "STOP: In-memory installed metadata simulation failed."
    }

    if ($ParsedMain.PrereqSHA1Hash -ne $Pending.PrereqSHA1Hash) {
        throw "STOP: In-memory PrereqSHA1Hash simulation failed."
    }

    if ($ParsedMain.bIsIncompleteInstall -ne $false) {
        throw "STOP: Simulated installed metadata remains incomplete."
    }

    if (
        $ParsedUE58.Count -ne 1 -or
        $ParsedUE58[0].AppVersion -ne $ExpectedAppVersion
    ) {
        throw "STOP: LauncherInstalled.dat simulation failed."
    }


    # ============================================================
    # DRY RUN RESULT
    # ============================================================

    Write-Host ""
    Write-Host "==========================================="
    Write-Host " STATUS: AFFECTED / CONDITIONS MATCH"
    Write-Host "==========================================="
    Write-Host ""
    Write-Host "Physical Engine:"
    Write-Host "  UE 5.8.2 / CL $ExpectedCL"
    Write-Host ""
    Write-Host "Installed metadata:"
    Write-Host "  $($Main.AppVersionString)"
    Write-Host ""
    Write-Host "Pending target:"
    Write-Host "  $($Pending.AppVersionString)"
    Write-Host ""
    Write-Host "LauncherInstalled.dat:"
    Write-Host "  $($UE58.AppVersion)"
    Write-Host ""
    Write-Host "In-memory commit simulation:"
    Write-Host "  PASSED"


    if (-not $ApplyChanges) {

        Write-Host ""
        Write-Host "==========================================="
        Write-Host " DRY RUN PASSED"
        Write-Host "==========================================="
        Write-Host "No files were modified, copied or deleted."
        Write-Host ""
        Write-Host "To apply the workaround:"
        Write-Host ""
        Write-Host 'Change: $ApplyChanges = $false'
        Write-Host 'To:     $ApplyChanges = $true'
        Write-Host ""
        Write-Host "Then run THIS SAME script again while"
        Write-Host "the Launcher is still in the Resume state."
        Write-Host "==========================================="

        return
    }


    # ============================================================
    # APPLY MODE
    # ============================================================
    #
    # From this point on changes are allowed.
    #
    # First create a complete backup while the Pending transaction
    # still exists. Only after that will the user be asked to close
    # Epic Games Launcher.
    #
    # ============================================================

    Write-Host ""
    Write-Host "==========================================="
    Write-Host " APPLY MODE - CREATING BACKUP"
    Write-Host "==========================================="


    $Desktop =
        [Environment]::GetFolderPath(
            [Environment+SpecialFolder]::Desktop
        )

    $Stamp =
        Get-Date -Format "yyyyMMdd_HHmmss"

    $BackupDir =
        Join-Path `
            $Desktop `
            "UE58_LauncherBackup_$Stamp"

    New-Item `
        -Path $BackupDir `
        -ItemType Directory |
        Out-Null


    Copy-Item `
        -LiteralPath $MainItemPath `
        -Destination (Join-Path $BackupDir "installed.item")

    Copy-Item `
        -LiteralPath $PendingItemPath `
        -Destination (Join-Path $BackupDir "pending.item")

    Copy-Item `
        -LiteralPath $Main.CompleteManifestPath `
        -Destination (Join-Path $BackupDir "installed.manifest")

    Copy-Item `
        -LiteralPath $PendingManifestPath `
        -Destination (Join-Path $BackupDir "pending.manifest")

    Copy-Item `
        -LiteralPath $LauncherDat `
        -Destination (Join-Path $BackupDir "LauncherInstalled.dat")

    Copy-Item `
        -LiteralPath $BuildVersionPath `
        -Destination (Join-Path $BackupDir "Build.version")


    Write-Host ""
    Write-Host "BACKUP CREATED:"
    Write-Host $BackupDir
    Write-Host ""
    Write-Host "Do NOT delete this backup until Epic provides"
    Write-Host "an official fix or you are satisfied with the result."


    # ============================================================
    # USER CONFIRMATION
    # ============================================================

    Write-Host ""
    Write-Host "Now completely EXIT Epic Games Launcher,"
    Write-Host "including the system tray icon."
    Write-Host ""
    Write-Host "The Pending files may disappear when Launcher exits."
    Write-Host "That is OK - copies are already stored in the backup."
    Write-Host ""

    $Confirm =
        Read-Host "After Launcher is fully closed, type APPLY"

    if ($Confirm -cne "APPLY") {
        Write-Host ""
        Write-Host "Cancelled."
        Write-Host "No Launcher metadata was modified."
        Write-Host "The backup directory was kept:"
        Write-Host $BackupDir

        return
    }


    # ============================================================
    # VERIFY LAUNCHER IS REALLY CLOSED
    # ============================================================

    $Running =
        Get-Process `
            EpicGamesLauncher,
            EpicWebHelper `
            -ErrorAction SilentlyContinue

    if ($Running) {
        throw "STOP: Epic Games Launcher or EpicWebHelper is still running. No metadata commit was performed."
    }


    # ============================================================
    # RE-READ BACKUP SOURCES
    # ============================================================

    $BackupMain =
        Read-JsonFile `
            (Join-Path $BackupDir "installed.item")

    $BackupPending =
        Read-JsonFile `
            (Join-Path $BackupDir "pending.item")


    if ($BackupPending.AppVersionString -ne $ExpectedAppVersion) {
        throw "STOP: Backup Pending metadata is not the expected UE 5.8.2 build."
    }


    # ============================================================
    # 11. PROMOTE COMPLETED PENDING MANIFEST TO INSTALLED MANIFEST
    # ============================================================

    Copy-Item `
        -LiteralPath (Join-Path $BackupDir "pending.manifest") `
        -Destination $BackupMain.CompleteManifestPath `
        -Force


    # ============================================================
    # 12. COMMIT INSTALLED ITEM METADATA
    # ============================================================

    #
    # Keep the structure and install options from the original
    # installed item.
    #
    # Only promote fields that are known to belong to the new build.
    #

    $BackupMain.AppVersionString =
        $BackupPending.AppVersionString

    $BackupMain.PrereqSHA1Hash =
        $BackupPending.PrereqSHA1Hash

    $BackupMain.bIsIncompleteInstall =
        $false


    $CommittedItemJson =
        $BackupMain |
        ConvertTo-Json -Depth 20


    $Utf8NoBom =
        New-Object System.Text.UTF8Encoding($false)

    [IO.File]::WriteAllText(
        $MainItemPath,
        $CommittedItemJson,
        $Utf8NoBom
    )


    # ============================================================
    # 13. UPDATE LauncherInstalled.dat
    # ============================================================

    #
    # Re-read the current file after Launcher shutdown so that
    # unrelated Launcher entries are preserved.
    #

    $CurrentLauncher =
        Read-JsonFile $LauncherDat

    $CurrentUE58 = @(
        $CurrentLauncher.InstallationList |
        Where-Object {
            $_.AppName -eq "UE_5.8"
        }
    )

    if ($CurrentUE58.Count -ne 1) {
        throw "STOP: UE_5.8 could not be uniquely resolved in LauncherInstalled.dat."
    }

    $CurrentUE58[0].AppVersion =
        $ExpectedAppVersion


    $CommittedLauncherJson =
        $CurrentLauncher |
        ConvertTo-Json -Depth 20

    [IO.File]::WriteAllText(
        $LauncherDat,
        $CommittedLauncherJson,
        $Utf8NoBom
    )


    # ============================================================
    # 14. REMOVE ONLY THE UE_5.8 PENDING TRANSACTION
    # ============================================================

    #
    # Launcher may already have removed these files on shutdown.
    # Therefore existence is checked first.
    #

    if (Test-Path $PendingItemPath) {
        Remove-Item `
            -LiteralPath $PendingItemPath
    }

    if (Test-Path $PendingManifestPath) {
        Remove-Item `
            -LiteralPath $PendingManifestPath
    }


    # ============================================================
    # 15. FINAL VERIFICATION
    # ============================================================

    $FinalMain =
        Read-JsonFile $MainItemPath

    $FinalLauncher =
        Read-JsonFile $LauncherDat

    $FinalUE58 = @(
        $FinalLauncher.InstallationList |
        Where-Object {
            $_.AppName -eq "UE_5.8"
        }
    )

    if ($FinalMain.AppVersionString -ne $ExpectedAppVersion) {
        throw "FINAL VERIFY FAILED: installed item version mismatch."
    }

    if ($FinalMain.bIsIncompleteInstall -ne $false) {
        throw "FINAL VERIFY FAILED: installed item is still incomplete."
    }

    if ($FinalMain.PrereqSHA1Hash -ne $BackupPending.PrereqSHA1Hash) {
        throw "FINAL VERIFY FAILED: PrereqSHA1Hash mismatch."
    }

    if (
        $FinalUE58.Count -ne 1 -or
        $FinalUE58[0].AppVersion -ne $ExpectedAppVersion
    ) {
        throw "FINAL VERIFY FAILED: LauncherInstalled.dat mismatch."
    }


    $FinalInstalledManifestHash =
        (Get-FileHash `
            $FinalMain.CompleteManifestPath `
            -Algorithm SHA256).Hash

    $BackupPendingManifestHash =
        (Get-FileHash `
            (Join-Path $BackupDir "pending.manifest") `
            -Algorithm SHA256).Hash

    if ($FinalInstalledManifestHash -ne $BackupPendingManifestHash) {
        throw "FINAL VERIFY FAILED: installed manifest does not match the completed Pending manifest."
    }


    Write-Host ""
    Write-Host "==========================================="
    Write-Host " WORKAROUND COMPLETED SUCCESSFULLY"
    Write-Host "==========================================="
    Write-Host ""
    Write-Host "Installed metadata:"
    Write-Host "  $($FinalMain.AppVersionString)"
    Write-Host ""
    Write-Host "Incomplete:"
    Write-Host "  $($FinalMain.bIsIncompleteInstall)"
    Write-Host ""
    Write-Host "LauncherInstalled.dat:"
    Write-Host "  $($FinalUE58[0].AppVersion)"
    Write-Host ""
    Write-Host "Pending item exists:"
    Write-Host "  $(Test-Path $PendingItemPath)"
    Write-Host ""
    Write-Host "Pending manifest exists:"
    Write-Host "  $(Test-Path $PendingManifestPath)"
    Write-Host ""
    Write-Host "Installed manifest SHA256:"
    Write-Host "  $FinalInstalledManifestHash"
    Write-Host ""
    Write-Host "Backup:"
    Write-Host "  $BackupDir"
    Write-Host ""
    Write-Host "You can now start Epic Games Launcher."
    Write-Host "It should display Unreal Engine 5.8.2 / Launch."
    Write-Host "==========================================="
}

Validation

Before publishing this workaround I tested the script in several modes.

  • The default read-only mode was tested against my repaired installation and correctly returned STATUS: ALREADY HEALTHY without modifying anything.
  • ApplyChanges = $true was also tested against the healthy installation and correctly exited without making changes.
  • The complete Apply workflow was then tested in an isolated sandbox created from the original files captured while the Launcher was affected:
Installed metadata: 5.8.1
Pending target:      5.8.2
Launcher metadata:   5.8.1

The sandbox test successfully performed the backup, manifest promotion, metadata reconciliation and Pending cleanup.

Final verification confirmed that the installed manifest SHA256 was identical to the original 5.8.2 Pending manifest SHA256:

9CB3C546C7D3C8C111D6D8AD69FE090714F5304FF1F81E099BD95B9F7493C86D

The real Epic Games Launcher and Unreal Engine installation were not modified during this sandbox test.

1 Like

Thanks for the share and getting down and dirty on the technicals. Just so you’re aware, this same behavior was kicking in with 5.8.2 → 5.8.3. Multiple update loops, couldn’t update plugins, etc. I tried several “fixes” to no avail, and was about to modify what you shared - but then magically seem to wake up and smell the roses.

Epic really needs to pin this down and squash this and related bugs ASAP. It’s affecting many people, it’s not a fringe issue. It seems to be loosely related to plugin update notification issues as well, where there are no actual updates to plugins, but launcher says there is. Or when users click on update, and nothing happens.

1 Like

Thanks for the report, we have a ticket in.

1 Like

UE-398557’s status has changed to ‘Ready for QA’. A member of the QA department is investigating the issue.

1 Like

Update: reproduced again with UE 5.8.3

I have now reproduced the same issue again while updating from UE 5.8.2 to UE 5.8.3.

Before starting the update, the installation was fully healthy:

Launcher metadata:      5.8.2-56702186
UnrealEditor.exe:       5.8.2.0 / CL-56702186
Build.version:          5.8.2 / CL-56702186
Pending item:           none
Pending manifest:       none

I then clicked Update once. The update downloaded and completed, but Epic Games Launcher returned to:

Unreal Engine 5.8.2
Resume

However, the physical Engine files were already updated successfully:

UnrealEditor.exe:
Numeric version: 5.8.3.0
ProductVersion: ++UE5+Release-5.8-CL-58210709

Engine\Build\Build.version:
Version: 5.8.3
Changelist: 58210709

Launcher metadata remained on:

5.8.2-56702186+++UE5+Release-5.8-Windows

while a new Pending transaction was created for:

5.8.3-58210709+++UE5+Release-5.8-Windows
bIsIncompleteInstall=True

The log shows the expected transition:

5.8.2-56702186 -> 5.8.3-58210709
Action: Update

and:

CurrentManifest: 5.8.2-56702186
InstallManifest: 5.8.3-58210709

BuildPatch then completes successfully:

ProcessSuccess: TRUE
ErrorCode: OK
FailureReasonText: The operation was successful.

but immediately afterwards:

IncompleteInstall=1 AutoResume=1
completed, waiting for manifest commit before starting next download

So this appears to reproduce the same manifest-commit failure on the next hotfix, not just the original 5.8.1 → 5.8.2 update.

One additional finding: the earlier 5.8.2 reproduction logged:

MainGameAppId mismatch
catalog=(null):(null):(null)
Correcting to catalog value

In the 5.8.3 reproduction this has changed to:

MainGameAppId mismatch
catalog=(empty)
Keeping on-disk value

Despite that changed handling, the manifest commit still fails. This suggests the MainGameAppId warning may not be the primary cause of the Resume loop.

I have preserved the complete 5.8.3 Pending .item, Pending manifest, installed manifest, Launcher metadata and Launcher log for the existing ticket.

Update: v2.0 generic UE 5.8.x workaround confirmed on 5.8.3

I have now reproduced the same Launcher manifest-commit failure again while updating from UE 5.8.2 to UE 5.8.3.

The first Update successfully installed the physical Engine files:

UnrealEditor.exe:
5.8.3.0
++UE5+Release-5.8-CL-58210709

Engine\Build\Build.version:
5.8.3
CL 58210709

but Epic Games Launcher remained on:

UE 5.8.2
Resume

with a new Pending transaction for:

5.8.3-58210709+++UE5+Release-5.8-Windows
bIsIncompleteInstall=True

I therefore updated the workaround script to v2.0.

What changed in v2.0

v2.0 no longer hardcodes a specific hotfix such as 5.8.2 or 5.8.3.

Instead, it:

  • detects the target UE 5.8.x version automatically from the active Pending transaction,
  • extracts the target patch version and changelist,
  • verifies that target independently against UnrealEditor.exe,
  • verifies it again against Engine\Build\Build.version,
  • checks LauncherInstalled.dat,
  • checks both installed and Pending manifests,
  • simulates the metadata commit in memory first,
  • runs in DRY-RUN / READ-ONLY mode by default,
  • creates a full backup before Apply mode,
  • requires explicit APPLY confirmation,
  • promotes the Pending manifest only after all validations pass,
  • updates only the UE 5.8-specific metadata,
  • removes only the matching UE 5.8 Pending transaction,
  • verifies the final manifest SHA256.

Validation

v2.0 was tested end-to-end on this real UE 5.8.2 → 5.8.3 failure.

Before Apply:

Installed metadata:
  5.8.2-56702186

Physical Engine:
  5.8.3 / CL 58210709

Pending target:
  5.8.3-58210709

LauncherInstalled.dat:
  5.8.2-56702186

DRY-RUN result:

STATUS: AFFECTED / CONDITIONS MATCH
Target verification: MATCH
In-memory commit simulation: PASSED
DRY RUN PASSED

Apply result:

WORKAROUND COMPLETED SUCCESSFULLY

Committed Engine:
  UE 5.8.3 / CL 58210709

Installed metadata:
  5.8.3-58210709+++UE5+Release-5.8-Windows

Incomplete:
  False

LauncherInstalled.dat:
  5.8.3-58210709+++UE5+Release-5.8-Windows

Pending item exists:
  False

Pending manifest exists:
  False

Final manifest verification:

Installed manifest SHA256:
65619BCDBC675EC4CE4C9D349B7CAAA7DA3F281A2085A2D0070533C0893BD06A

Target Pending manifest SHA256:
65619BCDBC675EC4CE4C9D349B7CAAA7DA3F281A2085A2D0070533C0893BD06A

After restarting Epic Games Launcher, it correctly displayed:

Unreal Engine 5.8.3
Launch

No additional Engine download was required.

Important

This is still an unofficial workaround, not an Epic fix.

The script is deliberately read-only by default:

$ApplyChanges = $false

Only change it to $true if DRY-RUN confirms that the physical Engine already matches the Pending target exactly.

 & {
    $ErrorActionPreference = "Stop"
    Set-StrictMode -Version Latest


    # ============================================================
    # UE 5.8.x LAUNCHER MANIFEST DIAGNOSTIC / WORKAROUND
    # Version 2.0
    # ============================================================
    #
    # DEFAULT = SAFE / READ-ONLY
    #
    # Leave this as $false first.
    #
    # Change to $true ONLY after DRY RUN PASSED and only if the
    # system matches the manifest-commit issue described here.
    #
    # This version automatically detects the target UE 5.8.x
    # hotfix and changelist from the Pending transaction.
    #
    # ============================================================

    $ApplyChanges = $false


    # ============================================================
    # SUPPORTED ENGINE BRANCH
    # ============================================================

    $SupportedMajor = 5
    $SupportedMinor = 8
    $ExpectedAppName = "UE_5.8"


    # ============================================================
    # EPIC LAUNCHER PATHS
    # ============================================================

    $ManifestRoot =
        "C:\ProgramData\Epic\EpicGamesLauncher\Data\Manifests"

    $PendingRoot =
        Join-Path $ManifestRoot "Pending"

    $LauncherDat =
        "C:\ProgramData\Epic\UnrealEngineLauncher\LauncherInstalled.dat"


    # ============================================================
    # HELPERS
    # ============================================================

    function Read-JsonFile {
        param(
            [Parameter(Mandatory = $true)]
            [string]$Path
        )

        Get-Content -LiteralPath $Path -Raw |
            ConvertFrom-Json
    }


    function Parse-UeAppVersion {
        param(
            [Parameter(Mandatory = $true)]
            [string]$VersionString
        )

        #
        # Example:
        #
        # 5.8.3-58210709+++UE5+Release-5.8-Windows
        #

        $Pattern =
            '^(?<Major>\d+)\.(?<Minor>\d+)\.(?<Patch>\d+)-(?<CL>\d+)\+\+\+UE5\+Release-(?<BranchMajor>\d+)\.(?<BranchMinor>\d+)-Windows$'

        $Match =
            [regex]::Match(
                $VersionString,
                $Pattern
            )

        if (-not $Match.Success) {
            throw "STOP: Unsupported AppVersionString format: $VersionString"
        }

        [PSCustomObject]@{
            Raw         = $VersionString
            Major       = [int]$Match.Groups["Major"].Value
            Minor       = [int]$Match.Groups["Minor"].Value
            Patch       = [int]$Match.Groups["Patch"].Value
            CL          = [int64]$Match.Groups["CL"].Value
            BranchMajor = [int]$Match.Groups["BranchMajor"].Value
            BranchMinor = [int]$Match.Groups["BranchMinor"].Value
        }
    }


    function Get-SemVersion {
        param($Parsed)

        [version](
            "{0}.{1}.{2}" -f `
            $Parsed.Major,
            $Parsed.Minor,
            $Parsed.Patch
        )
    }


    Write-Host ""
    Write-Host "================================================"
    Write-Host " UE 5.8.x LAUNCHER MANIFEST DIAGNOSTIC v2.0"
    Write-Host "================================================"

    if ($ApplyChanges) {
        Write-Host "MODE: APPLY CHANGES"
    }
    else {
        Write-Host "MODE: DRY RUN / READ-ONLY"
    }


    # ============================================================
    # 1. FIND INSTALLED UE_5.8 ITEM
    # ============================================================

    if (-not (Test-Path -LiteralPath $ManifestRoot)) {
        throw "STOP: Epic Games Launcher manifest directory was not found."
    }

    $MainCandidates = @()

    foreach (
        $File in
        Get-ChildItem `
            -LiteralPath $ManifestRoot `
            -Filter "*.item" `
            -File
    ) {
        try {
            $Data =
                Read-JsonFile $File.FullName

            if ($Data.AppName -eq $ExpectedAppName) {
                $MainCandidates +=
                    [PSCustomObject]@{
                        Path = $File.FullName
                        Data = $Data
                    }
            }
        }
        catch {
        }
    }


    if ($MainCandidates.Count -ne 1) {
        throw "STOP: Expected exactly one installed $ExpectedAppName item, found $($MainCandidates.Count)."
    }


    $MainItemPath =
        $MainCandidates[0].Path

    $Main =
        $MainCandidates[0].Data


    if ([string]::IsNullOrWhiteSpace($Main.AppVersionString)) {
        throw "STOP: Installed AppVersionString is empty."
    }


    $InstalledVersion =
        Parse-UeAppVersion $Main.AppVersionString


    if (
        $InstalledVersion.Major -ne $SupportedMajor -or
        $InstalledVersion.Minor -ne $SupportedMinor -or
        $InstalledVersion.BranchMajor -ne $SupportedMajor -or
        $InstalledVersion.BranchMinor -ne $SupportedMinor
    ) {
        throw "STOP: Installed metadata is not UE $SupportedMajor.$SupportedMinor.x."
    }


    Write-Host ""
    Write-Host "[1] Installed UE_5.8 metadata"
    Write-Host "Item       : $MainItemPath"
    Write-Host "Version    : $($Main.AppVersionString)"
    Write-Host "Parsed     : $($InstalledVersion.Major).$($InstalledVersion.Minor).$($InstalledVersion.Patch)"
    Write-Host "CL         : $($InstalledVersion.CL)"
    Write-Host "Incomplete : $($Main.bIsIncompleteInstall)"
    Write-Host "Location   : $($Main.InstallLocation)"


    # ============================================================
    # 2. FIND ACTIVE UE_5.8 PENDING TRANSACTION
    # ============================================================

    $PendingCandidates = @()

    if (Test-Path -LiteralPath $PendingRoot) {

        foreach (
            $File in
            Get-ChildItem `
                -LiteralPath $PendingRoot `
                -Filter "*.item" `
                -File `
                -ErrorAction SilentlyContinue
        ) {
            try {
                $Data =
                    Read-JsonFile $File.FullName

                if ($Data.AppName -eq $ExpectedAppName) {
                    $PendingCandidates +=
                        [PSCustomObject]@{
                            Path = $File.FullName
                            Data = $Data
                        }
                }
            }
            catch {
            }
        }
    }


    # ============================================================
    # 3. VERIFY PHYSICAL ENGINE
    # ============================================================

    if ([string]::IsNullOrWhiteSpace($Main.InstallLocation)) {
        throw "STOP: UE_5.8 InstallLocation is empty."
    }


    $EditorExe =
        Join-Path `
            $Main.InstallLocation `
            "Engine\Binaries\Win64\UnrealEditor.exe"

    $BuildVersionPath =
        Join-Path `
            $Main.InstallLocation `
            "Engine\Build\Build.version"


    if (-not (Test-Path -LiteralPath $EditorExe)) {
        throw "STOP: UnrealEditor.exe was not found."
    }

    if (-not (Test-Path -LiteralPath $BuildVersionPath)) {
        throw "STOP: Engine\Build\Build.version was not found."
    }


    $VI =
        (Get-Item -LiteralPath $EditorExe).VersionInfo

    $Build =
        Read-JsonFile $BuildVersionPath


    $PhysicalVersion =
        [version](
            "{0}.{1}.{2}" -f `
            $VI.FileMajorPart,
            $VI.FileMinorPart,
            $VI.FileBuildPart
        )


    $NumericFileVersion =
        "{0}.{1}.{2}.{3}" -f `
            $VI.FileMajorPart,
            $VI.FileMinorPart,
            $VI.FileBuildPart,
            $VI.FilePrivatePart


    Write-Host ""
    Write-Host "[2] Physical Unreal Engine"
    Write-Host "Numeric FileVersion : $NumericFileVersion"
    Write-Host "FileVersion string  : $($VI.FileVersion)"
    Write-Host "ProductVersion      : $($VI.ProductVersion)"
    Write-Host "Build.version       : $($Build.MajorVersion).$($Build.MinorVersion).$($Build.PatchVersion)"
    Write-Host "Build CL            : $($Build.Changelist)"


    if (
        $VI.FileMajorPart -ne $SupportedMajor -or
        $VI.FileMinorPart -ne $SupportedMinor
    ) {
        throw "STOP: Physical Engine is not UE $SupportedMajor.$SupportedMinor.x."
    }


    if (
        $Build.MajorVersion -ne $VI.FileMajorPart -or
        $Build.MinorVersion -ne $VI.FileMinorPart -or
        $Build.PatchVersion -ne $VI.FileBuildPart
    ) {
        throw "STOP: UnrealEditor.exe and Build.version disagree about the Engine version."
    }


    # ============================================================
    # 4. VERIFY INSTALLED MANIFEST
    # ============================================================

    if ([string]::IsNullOrWhiteSpace($Main.CompleteManifestPath)) {
        throw "STOP: CompleteManifestPath is empty."
    }


    if (-not (Test-Path -LiteralPath $Main.CompleteManifestPath)) {
        throw "STOP: Installed manifest was not found: $($Main.CompleteManifestPath)"
    }


    $InstalledManifestHash =
        (Get-FileHash `
            -LiteralPath $Main.CompleteManifestPath `
            -Algorithm SHA256).Hash


    Write-Host ""
    Write-Host "[3] Installed manifest"
    Write-Host "Path   : $($Main.CompleteManifestPath)"
    Write-Host "SHA256 : $InstalledManifestHash"


    # ============================================================
    # 5. READ LauncherInstalled.dat
    # ============================================================

    if (-not (Test-Path -LiteralPath $LauncherDat)) {
        throw "STOP: LauncherInstalled.dat was not found."
    }


    $Launcher =
        Read-JsonFile $LauncherDat


    $LauncherEntries = @(
        $Launcher.InstallationList |
        Where-Object {
            $_.AppName -eq $ExpectedAppName
        }
    )


    if ($LauncherEntries.Count -ne 1) {
        throw "STOP: Expected exactly one $ExpectedAppName entry in LauncherInstalled.dat."
    }


    $LauncherUE58 =
        $LauncherEntries[0]


    $LauncherVersion =
        Parse-UeAppVersion $LauncherUE58.AppVersion


    Write-Host ""
    Write-Host "[4] LauncherInstalled.dat"
    Write-Host "Version : $($LauncherUE58.AppVersion)"


    # ============================================================
    # 6. ALREADY HEALTHY?
    # ============================================================

    if ($PendingCandidates.Count -eq 0) {

        $BuildSemVer =
            [version](
                "{0}.{1}.{2}" -f `
                $Build.MajorVersion,
                $Build.MinorVersion,
                $Build.PatchVersion
            )


        $InstalledSemVer =
            Get-SemVersion $InstalledVersion


        $LauncherSemVer =
            Get-SemVersion $LauncherVersion


        if (
            $InstalledSemVer -eq $BuildSemVer -and
            $LauncherSemVer -eq $BuildSemVer -and
            $Main.bIsIncompleteInstall -eq $false -and
            $InstalledVersion.CL -eq [int64]$Build.Changelist -and
            $LauncherVersion.CL -eq [int64]$Build.Changelist
        ) {

            Write-Host ""
            Write-Host "================================================"
            Write-Host " STATUS: ALREADY HEALTHY"
            Write-Host "================================================"
            Write-Host "Physical Engine and Launcher metadata agree:"
            Write-Host (
                "UE {0}.{1}.{2} / CL {3}" -f `
                $Build.MajorVersion,
                $Build.MinorVersion,
                $Build.PatchVersion,
                $Build.Changelist
            )
            Write-Host ""
            Write-Host "No active UE 5.8 Pending transaction exists."
            Write-Host "No changes were made."
            Write-Host "================================================"

            return
        }


        Write-Host ""
        Write-Host "================================================"
        Write-Host " STATUS: PHYSICAL ENGINE / METADATA MISMATCH"
        Write-Host "================================================"
        Write-Host "Physical Engine is newer or different from"
        Write-Host "Launcher metadata, but no Pending transaction"
        Write-Host "is currently available."
        Write-Host ""
        Write-Host "No changes were made."
        Write-Host ""
        Write-Host "If Launcher is showing Update/Resume, start"
        Write-Host "that operation once and run this script again"
        Write-Host "while the Pending transaction still exists."
        Write-Host "================================================"

        return
    }


    if ($PendingCandidates.Count -ne 1) {
        throw "STOP: More than one UE_5.8 Pending item was found."
    }


    $PendingItemPath =
        $PendingCandidates[0].Path

    $Pending =
        $PendingCandidates[0].Data


    if ([string]::IsNullOrWhiteSpace($Pending.AppVersionString)) {
        throw "STOP: Pending AppVersionString is empty."
    }


    $Target =
        Parse-UeAppVersion $Pending.AppVersionString


    Write-Host ""
    Write-Host "[5] Active Pending transaction"
    Write-Host "Item       : $PendingItemPath"
    Write-Host "Version    : $($Pending.AppVersionString)"
    Write-Host "Target     : $($Target.Major).$($Target.Minor).$($Target.Patch)"
    Write-Host "Target CL  : $($Target.CL)"
    Write-Host "Incomplete : $($Pending.bIsIncompleteInstall)"
    Write-Host "PrereqSHA1 : $($Pending.PrereqSHA1Hash)"


    # ============================================================
    # 7. VALIDATE TARGET BRANCH
    # ============================================================

    if (
        $Target.Major -ne $SupportedMajor -or
        $Target.Minor -ne $SupportedMinor -or
        $Target.BranchMajor -ne $SupportedMajor -or
        $Target.BranchMinor -ne $SupportedMinor
    ) {
        throw "STOP: Pending target is outside the supported UE 5.8.x branch."
    }


    if ($Pending.bIsIncompleteInstall -ne $true) {
        throw "STOP: Pending UE_5.8 item is not marked as incomplete."
    }


    if (
        [string]::IsNullOrWhiteSpace($Pending.PrereqSHA1Hash) -or
        $Pending.PrereqSHA1Hash -notmatch '^[0-9A-Fa-f]{40}$'
    ) {
        throw "STOP: Pending PrereqSHA1Hash is invalid."
    }


    if (
        -not [string]::IsNullOrWhiteSpace($Pending.InstallLocation) -and
        $Pending.InstallLocation -ne $Main.InstallLocation
    ) {
        throw "STOP: Pending InstallLocation does not match installed UE_5.8."
    }


    $InstalledSemVer =
        Get-SemVersion $InstalledVersion

    $TargetSemVer =
        Get-SemVersion $Target


    if ($InstalledSemVer -gt $TargetSemVer) {
        throw "STOP: Installed Launcher metadata is newer than the Pending target."
    }


    # ============================================================
    # 8. VERIFY TARGET AGAINST PHYSICAL ENGINE
    # ============================================================

    if ($PhysicalVersion -ne $TargetSemVer) {
        throw (
            "STOP: Physical UnrealEditor.exe version " +
            "$PhysicalVersion does not match Pending target $TargetSemVer."
        )
    }


    if (
        $Build.MajorVersion -ne $Target.Major -or
        $Build.MinorVersion -ne $Target.Minor -or
        $Build.PatchVersion -ne $Target.Patch -or
        [int64]$Build.Changelist -ne $Target.CL
    ) {
        throw "STOP: Build.version does not match the Pending target."
    }


    if ($VI.ProductVersion -notlike "*CL-$($Target.CL)*") {
        throw "STOP: UnrealEditor.exe ProductVersion does not match Pending target CL $($Target.CL)."
    }


    Write-Host ""
    Write-Host "[6] Target verification"
    Write-Host "Pending target     : $TargetSemVer / CL $($Target.CL)"
    Write-Host "UnrealEditor.exe   : $PhysicalVersion / CL $($Target.CL)"
    Write-Host "Build.version      : $($Build.MajorVersion).$($Build.MinorVersion).$($Build.PatchVersion) / CL $($Build.Changelist)"
    Write-Host "Result             : MATCH"


    # ============================================================
    # 9. LOCATE PENDING MANIFEST
    # ============================================================

    if ([string]::IsNullOrWhiteSpace($Main.PendingManifestPath)) {
        throw "STOP: Installed UE_5.8 item has no PendingManifestPath."
    }


    $PendingManifestPath =
        $Main.PendingManifestPath


    if (-not (Test-Path -LiteralPath $PendingManifestPath)) {
        throw "STOP: Pending manifest was not found: $PendingManifestPath"
    }


    $PendingManifestHash =
        (Get-FileHash `
            -LiteralPath $PendingManifestPath `
            -Algorithm SHA256).Hash


    Write-Host ""
    Write-Host "[7] Pending manifest"
    Write-Host "Path   : $PendingManifestPath"
    Write-Host "SHA256 : $PendingManifestHash"


    # ============================================================
    # 10. VALIDATE LauncherInstalled.dat VERSION
    # ============================================================

    $LauncherSemVer =
        Get-SemVersion $LauncherVersion


    if ($LauncherSemVer -gt $TargetSemVer) {
        throw "STOP: LauncherInstalled.dat is newer than the Pending target."
    }


    if (
        $LauncherVersion.Major -ne $SupportedMajor -or
        $LauncherVersion.Minor -ne $SupportedMinor
    ) {
        throw "STOP: LauncherInstalled.dat is outside UE 5.8.x."
    }


    # ============================================================
    # 11. SIMULATE COMMIT IN MEMORY
    # ============================================================

    $SimMain =
        Read-JsonFile $MainItemPath


    $SimMain.AppVersionString =
        $Pending.AppVersionString

    $SimMain.PrereqSHA1Hash =
        $Pending.PrereqSHA1Hash

    $SimMain.bIsIncompleteInstall =
        $false


    $SimLauncher =
        Read-JsonFile $LauncherDat


    $SimLauncherEntries = @(
        $SimLauncher.InstallationList |
        Where-Object {
            $_.AppName -eq $ExpectedAppName
        }
    )


    if ($SimLauncherEntries.Count -ne 1) {
        throw "STOP: Simulation could not uniquely resolve UE_5.8."
    }


    $SimLauncherEntries[0].AppVersion =
        $Pending.AppVersionString


    $SimMainJson =
        $SimMain |
        ConvertTo-Json -Depth 20

    $SimLauncherJson =
        $SimLauncher |
        ConvertTo-Json -Depth 20


    $ParsedMain =
        $SimMainJson |
        ConvertFrom-Json

    $ParsedLauncher =
        $SimLauncherJson |
        ConvertFrom-Json


    $ParsedEntries = @(
        $ParsedLauncher.InstallationList |
        Where-Object {
            $_.AppName -eq $ExpectedAppName
        }
    )


    if ($ParsedMain.AppVersionString -ne $Pending.AppVersionString) {
        throw "STOP: In-memory installed metadata simulation failed."
    }


    if ($ParsedMain.PrereqSHA1Hash -ne $Pending.PrereqSHA1Hash) {
        throw "STOP: In-memory PrereqSHA1Hash simulation failed."
    }


    if ($ParsedMain.bIsIncompleteInstall -ne $false) {
        throw "STOP: Simulated installed metadata remains incomplete."
    }


    if (
        $ParsedEntries.Count -ne 1 -or
        $ParsedEntries[0].AppVersion -ne $Pending.AppVersionString
    ) {
        throw "STOP: LauncherInstalled.dat simulation failed."
    }


    # ============================================================
    # DIAGNOSTIC RESULT
    # ============================================================

    Write-Host ""
    Write-Host "================================================"
    Write-Host " STATUS: AFFECTED / CONDITIONS MATCH"
    Write-Host "================================================"
    Write-Host ""
    Write-Host "Launcher installed metadata:"
    Write-Host "  $($Main.AppVersionString)"
    Write-Host ""
    Write-Host "Physical Engine:"
    Write-Host "  UE $TargetSemVer / CL $($Target.CL)"
    Write-Host ""
    Write-Host "Pending target:"
    Write-Host "  $($Pending.AppVersionString)"
    Write-Host ""
    Write-Host "LauncherInstalled.dat:"
    Write-Host "  $($LauncherUE58.AppVersion)"
    Write-Host ""
    Write-Host "Installed manifest SHA256:"
    Write-Host "  $InstalledManifestHash"
    Write-Host ""
    Write-Host "Pending manifest SHA256:"
    Write-Host "  $PendingManifestHash"
    Write-Host ""
    Write-Host "In-memory commit simulation:"
    Write-Host "  PASSED"


    # ============================================================
    # DRY RUN STOP
    # ============================================================

    if (-not $ApplyChanges) {

        Write-Host ""
        Write-Host "================================================"
        Write-Host " DRY RUN PASSED"
        Write-Host "================================================"
        Write-Host "No files were modified, copied or deleted."
        Write-Host ""
        Write-Host "Detected target:"
        Write-Host "  UE $TargetSemVer / CL $($Target.CL)"
        Write-Host ""
        Write-Host "To apply the workaround:"
        Write-Host ""
        Write-Host 'Change: $ApplyChanges = $false'
        Write-Host 'To:     $ApplyChanges = $true'
        Write-Host ""
        Write-Host "Then run THIS SAME script again while the"
        Write-Host "Launcher still shows the failed Resume state."
        Write-Host "================================================"

        return
    }


    # ============================================================
    # APPLY MODE - CREATE BACKUP FIRST
    # ============================================================

    Write-Host ""
    Write-Host "================================================"
    Write-Host " APPLY MODE - CREATING BACKUP"
    Write-Host "================================================"


    $Desktop =
        [Environment]::GetFolderPath(
            [Environment+SpecialFolder]::Desktop
        )


    $Stamp =
        Get-Date -Format "yyyyMMdd_HHmmss"


    $BackupDir =
        Join-Path `
            $Desktop `
            (
                "UE58_ManifestFix_{0}_{1}" -f `
                $TargetSemVer.ToString(),
                $Stamp
            )


    New-Item `
        -Path $BackupDir `
        -ItemType Directory |
        Out-Null


    Copy-Item `
        -LiteralPath $MainItemPath `
        -Destination (Join-Path $BackupDir "installed.item")


    Copy-Item `
        -LiteralPath $PendingItemPath `
        -Destination (Join-Path $BackupDir "pending.item")


    Copy-Item `
        -LiteralPath $Main.CompleteManifestPath `
        -Destination (Join-Path $BackupDir "installed.manifest")


    Copy-Item `
        -LiteralPath $PendingManifestPath `
        -Destination (Join-Path $BackupDir "pending.manifest")


    Copy-Item `
        -LiteralPath $LauncherDat `
        -Destination (Join-Path $BackupDir "LauncherInstalled.dat")


    Copy-Item `
        -LiteralPath $BuildVersionPath `
        -Destination (Join-Path $BackupDir "Build.version")


    Write-Host ""
    Write-Host "BACKUP CREATED:"
    Write-Host $BackupDir


    # ============================================================
    # USER CONFIRMATION / CLOSE LAUNCHER
    # ============================================================

    Write-Host ""
    Write-Host "Now completely EXIT Epic Games Launcher,"
    Write-Host "including the system tray icon."
    Write-Host ""
    Write-Host "The Pending files may disappear when the"
    Write-Host "Launcher exits. That is OK because they are"
    Write-Host "already preserved in the backup."
    Write-Host ""


    $Confirm =
        Read-Host "After Launcher is fully closed, type APPLY"


    if ($Confirm -cne "APPLY") {

        Write-Host ""
        Write-Host "Cancelled."
        Write-Host "No Launcher metadata was modified."
        Write-Host ""
        Write-Host "Backup was kept:"
        Write-Host $BackupDir

        return
    }


    # ============================================================
    # VERIFY LAUNCHER IS CLOSED
    # ============================================================

    $Running =
        Get-Process `
            EpicGamesLauncher,
            EpicWebHelper `
            -ErrorAction SilentlyContinue


    if ($Running) {
        throw "STOP: Epic Games Launcher or EpicWebHelper is still running. No metadata commit was performed."
    }


    # ============================================================
    # RE-VALIDATE PHYSICAL ENGINE AFTER LAUNCHER SHUTDOWN
    # ============================================================

    $PostBuild =
        Read-JsonFile $BuildVersionPath


    if (
        $PostBuild.MajorVersion -ne $Target.Major -or
        $PostBuild.MinorVersion -ne $Target.Minor -or
        $PostBuild.PatchVersion -ne $Target.Patch -or
        [int64]$PostBuild.Changelist -ne $Target.CL
    ) {
        throw "STOP: Physical Engine changed after backup. No commit performed."
    }


    # ============================================================
    # RE-READ BACKUP SOURCES
    # ============================================================

    $BackupMain =
        Read-JsonFile `
            (Join-Path $BackupDir "installed.item")


    $BackupPending =
        Read-JsonFile `
            (Join-Path $BackupDir "pending.item")


    if ($BackupPending.AppVersionString -ne $Pending.AppVersionString) {
        throw "STOP: Backup Pending version does not match detected target."
    }


    if ($BackupPending.PrereqSHA1Hash -ne $Pending.PrereqSHA1Hash) {
        throw "STOP: Backup Pending PrereqSHA1Hash mismatch."
    }


    # ============================================================
    # 12. PROMOTE PENDING MANIFEST
    # ============================================================

    $BackupPendingManifest =
        Join-Path $BackupDir "pending.manifest"


    $BackupPendingHash =
        (Get-FileHash `
            -LiteralPath $BackupPendingManifest `
            -Algorithm SHA256).Hash


    if ($BackupPendingHash -ne $PendingManifestHash) {
        throw "STOP: Pending manifest backup hash mismatch."
    }


    Copy-Item `
        -LiteralPath $BackupPendingManifest `
        -Destination $BackupMain.CompleteManifestPath `
        -Force


    # ============================================================
    # 13. COMMIT INSTALLED ITEM METADATA
    # ============================================================

    #
    # Keep the installed item's structure/options.
    # Promote only fields known to belong to the completed build.
    #

    $BackupMain.AppVersionString =
        $BackupPending.AppVersionString

    $BackupMain.PrereqSHA1Hash =
        $BackupPending.PrereqSHA1Hash

    $BackupMain.bIsIncompleteInstall =
        $false


    $Utf8NoBom =
        New-Object System.Text.UTF8Encoding($false)


    [IO.File]::WriteAllText(
        $MainItemPath,
        ($BackupMain | ConvertTo-Json -Depth 20),
        $Utf8NoBom
    )


    # ============================================================
    # 14. UPDATE LauncherInstalled.dat
    # ============================================================

    #
    # Read the current file after Launcher shutdown so unrelated
    # application entries are preserved.
    #

    $CurrentLauncher =
        Read-JsonFile $LauncherDat


    $CurrentEntries = @(
        $CurrentLauncher.InstallationList |
        Where-Object {
            $_.AppName -eq $ExpectedAppName
        }
    )


    if ($CurrentEntries.Count -ne 1) {
        throw "STOP: UE_5.8 could not be uniquely resolved in LauncherInstalled.dat."
    }


    $CurrentEntries[0].AppVersion =
        $BackupPending.AppVersionString


    [IO.File]::WriteAllText(
        $LauncherDat,
        ($CurrentLauncher | ConvertTo-Json -Depth 20),
        $Utf8NoBom
    )


    # ============================================================
    # 15. REMOVE ONLY THIS UE_5.8 PENDING TRANSACTION
    # ============================================================

    if (Test-Path -LiteralPath $PendingItemPath) {
        Remove-Item `
            -LiteralPath $PendingItemPath
    }


    if (Test-Path -LiteralPath $PendingManifestPath) {
        Remove-Item `
            -LiteralPath $PendingManifestPath
    }


    # ============================================================
    # 16. FINAL VERIFICATION
    # ============================================================

    $FinalMain =
        Read-JsonFile $MainItemPath


    $FinalLauncher =
        Read-JsonFile $LauncherDat


    $FinalEntries = @(
        $FinalLauncher.InstallationList |
        Where-Object {
            $_.AppName -eq $ExpectedAppName
        }
    )


    if ($FinalMain.AppVersionString -ne $BackupPending.AppVersionString) {
        throw "FINAL VERIFY FAILED: installed AppVersionString mismatch."
    }


    if ($FinalMain.bIsIncompleteInstall -ne $false) {
        throw "FINAL VERIFY FAILED: installed item remains incomplete."
    }


    if ($FinalMain.PrereqSHA1Hash -ne $BackupPending.PrereqSHA1Hash) {
        throw "FINAL VERIFY FAILED: PrereqSHA1Hash mismatch."
    }


    if (
        $FinalEntries.Count -ne 1 -or
        $FinalEntries[0].AppVersion -ne $BackupPending.AppVersionString
    ) {
        throw "FINAL VERIFY FAILED: LauncherInstalled.dat mismatch."
    }


    $FinalInstalledManifestHash =
        (Get-FileHash `
            -LiteralPath $FinalMain.CompleteManifestPath `
            -Algorithm SHA256).Hash


    if ($FinalInstalledManifestHash -ne $BackupPendingHash) {
        throw "FINAL VERIFY FAILED: installed manifest does not match the Pending target manifest."
    }


    if (Test-Path -LiteralPath $PendingItemPath) {
        throw "FINAL VERIFY FAILED: Pending item still exists."
    }


    if (Test-Path -LiteralPath $PendingManifestPath) {
        throw "FINAL VERIFY FAILED: Pending manifest still exists."
    }


    Write-Host ""
    Write-Host "================================================"
    Write-Host " WORKAROUND COMPLETED SUCCESSFULLY"
    Write-Host "================================================"
    Write-Host ""
    Write-Host "Committed Engine:"
    Write-Host "  UE $TargetSemVer / CL $($Target.CL)"
    Write-Host ""
    Write-Host "Installed metadata:"
    Write-Host "  $($FinalMain.AppVersionString)"
    Write-Host ""
    Write-Host "Incomplete:"
    Write-Host "  $($FinalMain.bIsIncompleteInstall)"
    Write-Host ""
    Write-Host "LauncherInstalled.dat:"
    Write-Host "  $($FinalEntries[0].AppVersion)"
    Write-Host ""
    Write-Host "Installed manifest SHA256:"
    Write-Host "  $FinalInstalledManifestHash"
    Write-Host ""
    Write-Host "Target manifest SHA256:"
    Write-Host "  $BackupPendingHash"
    Write-Host ""
    Write-Host "Pending item exists:"
    Write-Host "  $(Test-Path $PendingItemPath)"
    Write-Host ""
    Write-Host "Pending manifest exists:"
    Write-Host "  $(Test-Path $PendingManifestPath)"
    Write-Host ""
    Write-Host "Backup:"
    Write-Host "  $BackupDir"
    Write-Host ""
    Write-Host "You can now start Epic Games Launcher."
    Write-Host "================================================"
}