You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug] Windows installer 2.0.290 fails with error 1603 during ValidateInstallRootPreview #701
I'm experiencing an installation failure with Vibepollo 2.0.290 on Windows.
When attempting to reinstall Vibepollo, the installer successfully removes the existing installation but then fails with MSI error 1603.
The installation is blocked during the "ValidateInstallRootPreview" custom action.
Steps to reproduce
Start with Vibepollo installed in "C:\Program Files\Apollo".
Run the Windows installer to reinstall/update Vibepollo.
The existing installation is removed successfully (exit code 0).
The new installation fails during directory validation.
Actual behavior
The installer returns:
The install folder or an existing product directory is unsafe,
writable by an untrusted account, or contains a reparse point.
Close the host and run the bootstrapper from an elevated account.
CustomAction ValidateInstallRootPreview returned actual error code 1603
Action ended: ValidateInstallRootPreview. Return value 3.
The previous version was successfully removed (exit code 0).
Previous installations of version 2.0.290 succeeded on October 25 and October 26.
The latest MSI log also reports "MsiSystemRebootPending = 1".
Could you help identify what causes "ValidateInstallRootPreview" to reject the installation directory even when the installer is running with elevated privileges?
Is there a safe way to determine the specific directory or permission that is triggering this validation?
I can provide the full installation logs if additional information is needed.
Expected Behavior
The installer should complete successfully, or provide a specific explanation identifying which directory, permission, or reparse point is causing the security validation to fail.
Ideally, the existing installation should not be removed before this validation succeeds.
We are comparing this failure across Vibeshine #391, Vibeshine #392, and Vibepollo #701. The current installer log does not identify the rejected path or Windows error. This read-only diagnostic collects the missing details automatically.
Run once and attach one report
Open Windows PowerShell as administrator from the Start menu.
Copy and paste this entire command, then press Enter. It downloads the diagnostic embedded in this comment, verifies its SHA-256 checksum, and runs it. There is no need to save a script manually or change your system's execution-policy setting.
& { $ErrorActionPreference='Stop'; try { $c=Invoke-RestMethod -Uri 'https://api.github.com/repos/Nonary/Vibepollo/issues/comments/6561597946' -Headers @{'User-Agent'='InstallerFolderDiagnostic'}; $m=[regex]::Match($c.body,'(?s)<!-- installer-diagnostic-v2 -->\s*```powershell\r?\n(.*?)\r?\n```'); if(-not $m.Success){throw 'Diagnostic script was not found in the issue comment.'}; $s=$m.Groups[1].Value.Replace("`r",'').Trim(); $h=[Security.Cryptography.SHA256]::Create(); $hash=[BitConverter]::ToString($h.ComputeHash([Text.Encoding]::UTF8.GetBytes($s))).Replace('-','').ToLowerInvariant(); $h.Dispose(); if($hash -ne 'f5219668a649988a598d600b8a0c4d3b7646a9e4e5fdb5da759122699c86d4b2'){throw 'Diagnostic script checksum mismatch. Please report this instead of running it.'}; $p=Join-Path $env:TEMP ('installer-folder-diagnostic-'+[guid]::NewGuid().ToString('N')+'.ps1'); [IO.File]::WriteAllText($p,$s,(New-Object Text.UTF8Encoding($false))); & "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NoProfile -ExecutionPolicy Bypass -File $p } catch { Write-Host $_ -ForegroundColor Red } }
When it finishes, File Explorer opens with the report selected. Drag that text file into a reply here. If Explorer cannot open, the report's full path is printed in PowerShell. If the command fails, paste the displayed error instead.
The collector automatically checks the normal installation locations plus locations found in relevant installer registrations. It records ancestor and installation-file ownership/permissions, raw security descriptors, reparse points, native file-open/security-query errors, MSI product/directory identities, Windows build, elevation, and reboot markers. Missing installation folders are recorded too. No separate screenshots, registry exports, or commands are needed for the initial collection.
It does not change permissions, uninstall anything, stop/start services, execute installed application code, or read configuration/credential file contents. It writes the diagnostic script, its text report, and temporary compiler files; it opens File Explorer at the end. It may take a few minutes for a large installation. The report contains paths/account SIDs, so review or redact identifying details before uploading it. The report is never uploaded automatically.
Validation so far: PowerShell syntax, embedded C# compilation, and mocked permission-classification checks passed. The native Windows probes still need an affected Windows machine; the collector records probe failures in the report. This gathers the evidence needed to distinguish an unsafe directory from a guard compatibility problem; it does not assume either outcome or guarantee a fix from one report.
Diagnostic source (automatically downloaded by the command above)
# Windows PowerShell 5.1+. Read-only except for the report and compiler temporary files.# Does not start/stop services, change ACLs, or read file contents.
[CmdletBinding()]
param(
[string[]]$InstallRoot=@(),
[ValidateRange(1,100000)][int]$MaxEntries=20000
)
$ErrorActionPreference='Stop'$report=Join-Path$env:TEMP ('installer-folder-diagnostic-{0}.txt'-f (Get-Date-Format 'yyyyMMdd-HHmmss'))
$lines=New-Object'System.Collections.Generic.List[string]'functionLog([string]$Text) { $lines.Add($Text) }
$identity= [Security.Principal.WindowsIdentity]::GetCurrent()
$principal=New-Object Security.Principal.WindowsPrincipal($identity)
Log 'Vibeshine / Vibepollo installer folder diagnostic v2'
Log ('Time UTC: '+ [DateTime]::UtcNow.ToString('o'))
Log ('Elevated: '+$principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
Log ('PowerShell: '+$PSVersionTable.PSVersion.ToString())
Log ('OS: '+ [Environment]::OSVersion.VersionString)
Log ('64-bit PowerShell: '+ [Environment]::Is64BitProcess)
Log ('Process user SID: '+$identity.User.Value)
Log ('ProgramFiles environment: '+$env:ProgramFiles)
Log ('ProgramW6432 environment: '+$env:ProgramW6432)
Log ('ProgramFiles(x86) environment: '+${env:ProgramFiles(x86)})
Log 'Read-only inspection; no file contents collected. Paths and account SIDs may identify users.'
Log 'The preview-open probe closes each handle immediately; this is not a full reproduction of the MSI guard.'# Match the native guard's preview access/share flags, without following a# final-component reparse point and without creating or modifying any object.try {
if (-not ('InstallerFolderDiagnostic.Native'-as [type])) {
Add-Type-TypeDefinition @'using System;using System.Runtime.InteropServices;using System.Text;using System.Collections.Generic;namespace InstallerFolderDiagnostic { public class ProbeResult { public int OpenError; public uint SecurityError; public int DescriptorError; public string Sddl; } public static class Native { [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] private static extern IntPtr CreateFileW(string path, uint access, uint share, IntPtr security, uint disposition, uint flags, IntPtr template); [DllImport("kernel32.dll", SetLastError=true)] private static extern bool CloseHandle(IntPtr handle); [DllImport("advapi32.dll", SetLastError=true)] private static extern uint GetSecurityInfo(IntPtr handle, int objectType, uint information, out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor); [DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)] private static extern bool ConvertSecurityDescriptorToStringSecurityDescriptorW( IntPtr descriptor, uint revision, uint information, out IntPtr text, out uint length); [DllImport("kernel32.dll")] private static extern IntPtr LocalFree(IntPtr memory); [DllImport("msi.dll", CharSet=CharSet.Unicode)] private static extern uint MsiEnumRelatedProductsW(string upgrade, uint reserved, uint index, StringBuilder code); [DllImport("msi.dll", CharSet=CharSet.Unicode)] private static extern uint MsiGetProductInfoExW(string code, string sid, uint context, string property, StringBuilder value, ref uint length); [DllImport("msi.dll", CharSet=CharSet.Unicode)] private static extern int MsiGetComponentPathExW(string product, string component, string sid, uint context, StringBuilder value, ref uint length); public static string[] RelatedProducts(string upgrade) { List<string> codes = new List<string>(); for (uint i=0; i<4096; i++) { StringBuilder code = new StringBuilder(39); uint result = MsiEnumRelatedProductsW(upgrade, 0, i, code); if (result == 259) return codes.ToArray(); if (result != 0) throw new System.ComponentModel.Win32Exception((int)result); codes.Add(code.ToString()); } throw new Exception("MSI enumeration limit reached"); } public static string ProductInfo(string code, string property) { StringBuilder value = new StringBuilder(32768); uint length=32768; uint result = MsiGetProductInfoExW(code, null, 4, property, value, ref length); if (result != 0) throw new System.ComponentModel.Win32Exception((int)result); return value.ToString(); } public static string ComponentPath(string code, string component) { StringBuilder value = new StringBuilder(32768); uint length=32768; int state = MsiGetComponentPathExW(code, component, null, 4, value, ref length); return state == 3 ? value.ToString() : "<MSI component state: " + state + ">"; } public static ProbeResult Preview(string path) { ProbeResult result = new ProbeResult(); IntPtr h = CreateFileW(path, 0x00020100, 3, IntPtr.Zero, 3, 0x02200000, IntPtr.Zero); if (h == new IntPtr(-1)) { result.OpenError=Marshal.GetLastWin32Error(); return result; } IntPtr descriptor=IntPtr.Zero, text=IntPtr.Zero; try { IntPtr owner, group, dacl, sacl; result.SecurityError=GetSecurityInfo(h, 1, 5, out owner, out group, out dacl, out sacl, out descriptor); if (result.SecurityError == 0) { uint length; if (ConvertSecurityDescriptorToStringSecurityDescriptorW(descriptor,1,5,out text,out length)) result.Sddl=Marshal.PtrToStringUni(text); else result.DescriptorError=Marshal.GetLastWin32Error(); } return result; } finally { if (text != IntPtr.Zero) LocalFree(text); if (descriptor != IntPtr.Zero) LocalFree(descriptor); CloseHandle(h); } } }}'@
}
$nativeAvailable=$true
} catch {
$nativeAvailable=$false
Log ('Native preview-open probe unavailable: '+$_.Exception.Message)
}
$trusted=@('S-1-5-18','S-1-5-32-544','S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464')
$roots=New-Object'System.Collections.Generic.List[string]'functionAdd-Root([string]$Path) {
if ([string]::IsNullOrWhiteSpace($Path)) { return }
if ($Path-notmatch'^[a-zA-Z]:\\') { Log ('Non-local/invalid registered path (not inspected): '+$Path); return }
try {
$full= [IO.Path]::GetFullPath($Path).TrimEnd('\')
if ($full.Length-le3) { Log ('Drive root registration (not recursively inspected): '+$full); return }
if (-not$roots.Contains($full)) { $roots.Add($full) }
} catch { Log ('Invalid registered path: '+$Path+'; '+$_.Exception.Message) }
}
foreach ($rootin$InstallRoot) { Add-Root$root }
foreach ($basein@($env:ProgramW6432,$env:ProgramFiles,${env:ProgramFiles(x86)})) {
if ($base) { foreach ($namein@('Sunshine','Apollo','Vibeshine','Vibepollo')) { Add-Root (Join-Path$base$name) } }
}
Log ''
Log 'Relevant uninstall registrations (read-only; no uninstall commands executed):'foreach ($hivein@([Microsoft.Win32.RegistryHive]::LocalMachine,[Microsoft.Win32.RegistryHive]::CurrentUser)) {
foreach ($viewin@([Microsoft.Win32.RegistryView]::Registry64,[Microsoft.Win32.RegistryView]::Registry32)) {
$baseKey=$null; $uninstall=$nulltry {
$baseKey= [Microsoft.Win32.RegistryKey]::OpenBaseKey($hive,$view)
$uninstall=$baseKey.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',$false)
if ($null-eq$uninstall) { continue }
foreach ($keyNamein$uninstall.GetSubKeyNames()) {
$key=$nulltry {
$key=$uninstall.OpenSubKey($keyName,$false)
if ($null-eq$key) { continue }
$displayName= [string]$key.GetValue('DisplayName')
if ($displayName-notmatch'(?i)sunshine|vibeshine|apollo|vibepollo'-and$keyName-notmatch'^(Sunshine|Vibeshine|Apollo|Vibepollo)$') { continue }
Log ('REGISTRY: '+$hive+'/'+$view+'/'+$keyName)
foreach ($fieldin@('DisplayName','DisplayVersion','WindowsInstaller','ProductCode','InstallLocation')) {
Log ($field+': '+ [string]$key.GetValue($field))
if ($null-ne$key.GetValue($field)) { Log ($field+' registry type: '+$key.GetValueKind($field)) }
}
Add-Root ([string]$key.GetValue('InstallLocation'))
} catch { Log ('Registration read error: '+$_.Exception.Message) }
finally { if ($key) { $key.Dispose() } }
}
} catch { Log ('Registry read error: '+$_.Exception.Message) }
finally { if ($uninstall) { $uninstall.Dispose() }; if ($baseKey) { $baseKey.Dispose() } }
}
}
if ($nativeAvailable) {
foreach ($upgradein@('{C2C36624-2D9C-4AFD-9C79-6B7861AE4A0D}','{E3FA501A-85F8-4187-85A7-D6E6BDC7EDA1}')) {
try {
$products=@([InstallerFolderDiagnostic.Native]::RelatedProducts($upgrade))
Log ('MSI upgrade family: '+$upgrade+'; related products: '+$products.Count)
foreach ($productin$products) {
Log ('MSI product: '+$product)
foreach ($fieldin@('ProductName','VersionString','InstallLocation','LocalPackage')) {
try {
$value= [InstallerFolderDiagnostic.Native]::ProductInfo($product,$field)
Log ($field+': '+$value)
if ($field-eq'InstallLocation') { Add-Root$value }
} catch { Log ($field+' query error: '+$_.Exception.Message) }
}
foreach ($componentin@('{B6D8A6A3-63B7-4C3F-8A2C-2C8F2B2F3B61}','{640D87CF-69A4-4F54-AE29-941F78D020B2}')) {
try {
$value= [InstallerFolderDiagnostic.Native]::ComponentPath($product,$component)
Log ('Directory component '+$component+': '+$value)
if ($value-notmatch'^<') { Add-Root$value }
} catch { Log ('Directory component query error: '+$_.Exception.Message) }
}
}
} catch { Log ('Related MSI product query error: '+$_.Exception.Message) }
}
}
$windows=Get-ItemProperty-LiteralPath 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'-ErrorAction SilentlyContinue
if ($windows) { Log ('Windows build: {0}.{1}; display version: {2}'-f$windows.CurrentBuildNumber,$windows.UBR,$windows.DisplayVersion) }
foreach ($pendingKeyin@('HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending','HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired')) {
Log ('Reboot marker '+$pendingKey+': '+ (Test-Path-LiteralPath $pendingKey))
}
$seen=@{}
$script:count=0$script:flagged=0functionInspect-Path([string]$Path, [bool]$Strict, [bool]$AlwaysPrint) {
if ($seen.ContainsKey($Path)) { return$seen[$Path] }
$seen[$Path] =$null$script:count++$details=New-Object'System.Collections.Generic.List[string]'$reasons=New-Object'System.Collections.Generic.List[string]'$item=$nulltry {
$item=Get-Item-LiteralPath $Path-Force -ErrorAction Stop
$details.Add('Attributes: '+$item.Attributes)
if ($item.Attributes-band [IO.FileAttributes]::ReparsePoint) {
$reasons.Add('Reparse point; descendants will not be inspected')
}
if ($nativeAvailable) {
$probe= [InstallerFolderDiagnostic.Native]::Preview($Path)
$details.Add('Native preview-open Win32 error: '+$probe.OpenError)
$details.Add('Native GetSecurityInfo error: '+$probe.SecurityError)
$details.Add('Native SDDL conversion error: '+$probe.DescriptorError)
$details.Add('Native owner/DACL SDDL: '+$probe.Sddl)
if ($probe.OpenError-ne0) { $reasons.Add('Native preview-open failed: '+$probe.OpenError) }
if ($probe.SecurityError-ne0) { $reasons.Add('Native security query failed: '+$probe.SecurityError) }
if ($probe.DescriptorError-ne0) { $reasons.Add('Native SDDL conversion failed: '+$probe.DescriptorError) }
}
$acl=Get-Acl-LiteralPath $Path-ErrorAction Stop
$details.Add('SDDL: '+$acl.Sddl)
$owner=$acl.GetOwner([Security.Principal.SecurityIdentifier]).Value
$details.Add('Owner SID: '+$owner)
$details.Add('DACL protected: '+$acl.AreAccessRulesProtected)
if ($trusted-notcontains$owner) { $reasons.Add('Owner is outside native guard whitelist') }
$rules=$acl.GetAccessRules($true,$true, [Security.Principal.SecurityIdentifier])
foreach ($rulein$rules) {
$sid=$rule.IdentityReference.Value$mask= ([long]$rule.FileSystemRights) -band4294967295L$inheritOnly= ($rule.PropagationFlags-band [Security.AccessControl.PropagationFlags]::InheritOnly) -ne0$details.Add(('ACE: SID={0}; type={1}; mask=0x{2:X8}; inherited={3}; inheritance={4}; propagation={5}'-f$sid,$rule.AccessControlType,$mask,$rule.IsInherited,$rule.InheritanceFlags,$rule.PropagationFlags))
if ($rule.AccessControlType-ne [Security.AccessControl.AccessControlType]::Allow) { continue }
if (($inheritOnly-and-not$Strict) -or ($trusted-contains$sid) -or ($inheritOnly-and$sid-eq'S-1-3-0')) { continue }
$unsafeMask=0x500D0040Lif ($Strict) { $unsafeMask=$unsafeMask-bor0x116L }
if (($mask-band$unsafeMask) -ne0) { $reasons.Add('Allow ACE rejected by native guard mask: '+$sid) }
}
} catch {
$reasons.Add('Inspection error: '+$_.Exception.GetType().FullName +'; HResult='+$_.Exception.HResult+'; '+$_.Exception.Message)
}
if ($AlwaysPrint-or$reasons.Count-gt0) {
Log ''
Log ('PATH: '+$Path)
Log ('Strict mutation check: '+$Strict)
foreach ($reasonin$reasons) { Log ('CHECK: '+$reason) }
foreach ($detailin$details) { Log $detail }
}
if ($reasons.Count-gt0) { $script:flagged++ }
$seen[$Path] =$itemreturn$item
}
try {
foreach ($rootin$roots) {
$root= [IO.Path]::GetFullPath($root).TrimEnd('\')
Log ('Requested install root: '+$root)
$ancestors=New-Object'System.Collections.Generic.List[string]'$path=$rootwhile ($path) {
$ancestors.Insert(0,$path)
$parent= [IO.Directory]::GetParent($path)
if ($null-eq$parent) { break }
$path=$parent.FullName
}
$rootItem=$null$redirected=$falseforeach ($pathin$ancestors) {
$item= Inspect-Path $path ($path.Length-gt3) $trueif ($null-ne$item-and ($item.Attributes-band [IO.FileAttributes]::ReparsePoint)) { $redirected=$true; break }
if ($path-eq$root) { $rootItem=$item }
}
if ($redirected-or$null-eq$rootItem-or-not$rootItem.PSIsContainer) { continue }
$safeToRecurse=$falseforeach ($basein@($env:ProgramW6432,$env:ProgramFiles,${env:ProgramFiles(x86)})) {
if ($base-and$root.StartsWith($base.TrimEnd('\') +'\',[StringComparison]::OrdinalIgnoreCase)) { $safeToRecurse=$true }
}
foreach ($explicitRootin$InstallRoot) { if ($root-eq$explicitRoot.TrimEnd('\')) { $safeToRecurse=$true } }
if (-not$safeToRecurse) { Log ('Only root/ancestors inspected outside Program Files: '+$root); continue }
$pending=New-Object'System.Collections.Generic.Stack[string]'$pending.Push($root)
while ($pending.Count-gt0-and$script:count-lt$MaxEntries) {
Write-Progress-Activity 'Collecting installer diagnostics'-Status ('Inspected '+$script:count+' entries')
$directory=$pending.Pop()
try {
foreach ($childin (Get-ChildItem-LiteralPath $directory-Force -ErrorAction Stop)) {
if ($script:count-ge$MaxEntries) { break }
$item= Inspect-Path $child.FullName$true$falseif ($null-ne$item-and$item.PSIsContainer-and-not ($item.Attributes-band [IO.FileAttributes]::ReparsePoint)) { $pending.Push($item.FullName) }
}
} catch { Log ('Enumeration error at '+$directory+': '+$_.Exception.Message) }
}
if ($script:count-ge$MaxEntries) { Log 'SCAN LIMIT REACHED; descendant inspection is incomplete.' }
}
} catch { Log ('Diagnostic error: '+$_.Exception.Message) }
Log ''
Log ('Inspected entries: '+$script:count+'; entries with checks/errors: '+$script:flagged)
Log 'No flagged entries does not prove MSI validation will pass: races and concurrent handle pinning are not fully reproduced.'$lines|Set-Content-LiteralPath $report-Encoding UTF8
Write-Progress-Activity 'Collecting installer diagnostics'-Completed
Write-Host ('Report saved to: '+$report)
Write-Host'Review paths and account SIDs before attaching the report to your issue.'try { Start-Process-FilePath (Join-Path$env:WINDIR'explorer.exe') -ArgumentList ('/select,"'+$report+'"') } catch { Write-Host'Could not open File Explorer. Use the report path above.' }
I ran the read-only installer folder diagnostic from an elevated PowerShell session. The installer still fails with MSI error 1603 during ValidateInstallRootPreview, with the message that the install folder or an existing product directory is unsafe, writable by an untrusted account, or contains a reparse point.
The diagnostic results for this machine:
Windows 11, display version 26H2, build 26300.9818
Diagnostic ran elevated: True
Reboot-pending markers: False
For C:\Program Files\Apollo and its parent directories:
Native preview-open Win32 error: 0
Native GetSecurityInfo error: 0
Native SDDL conversion error: 0
Apollo directory owner: SYSTEM (S-1-5-18)
No suspicious ACL entries or reparse points were reported.
The Vibepollo uninstall registration still exists at HKLM\...\Vibepollo and points to C:\Program Files\Apollo\, with version 2.0.2-daily.d20261014.1+66e169c325da.
However, the MSI product identity discovered by the diagnostic is not recognized by Windows Installer as currently installed (MsiGetProductInfoEx returns “This action is only valid for products that are currently installed”).
So far, the diagnostic does not show an obvious permissions or reparse-point problem in the Apollo install directory. The stale-looking uninstall/MSI registration may be relevant, but I cannot confirm that it is the cause.
Could you please advise what to try next, or whether you can provide a more detailed/instrumented installer build that identifies exactly which path or validation check is causing ValidateInstallRootPreview to fail?
I have the complete diagnostic report available and can attach/paste it if needed.
Activity
markmghali commentedon Nov 1, 2026
same issue
jcbrizzy commentedon Nov 1, 2026
Getting something similar w/ 2.0.3 ..... awesome. Uninstalled everything.. and won't install.
Nonary commentedon Nov 1, 2026
We are comparing this failure across Vibeshine #391, Vibeshine #392, and Vibepollo #701. The current installer log does not identify the rejected path or Windows error. This read-only diagnostic collects the missing details automatically.
Run once and attach one report
& { $ErrorActionPreference='Stop'; try { $c=Invoke-RestMethod -Uri 'https://api.github.com/repos/Nonary/Vibepollo/issues/comments/6561597946' -Headers @{'User-Agent'='InstallerFolderDiagnostic'}; $m=[regex]::Match($c.body,'(?s)<!-- installer-diagnostic-v2 -->\s*```powershell\r?\n(.*?)\r?\n```'); if(-not $m.Success){throw 'Diagnostic script was not found in the issue comment.'}; $s=$m.Groups[1].Value.Replace("`r",'').Trim(); $h=[Security.Cryptography.SHA256]::Create(); $hash=[BitConverter]::ToString($h.ComputeHash([Text.Encoding]::UTF8.GetBytes($s))).Replace('-','').ToLowerInvariant(); $h.Dispose(); if($hash -ne 'f5219668a649988a598d600b8a0c4d3b7646a9e4e5fdb5da759122699c86d4b2'){throw 'Diagnostic script checksum mismatch. Please report this instead of running it.'}; $p=Join-Path $env:TEMP ('installer-folder-diagnostic-'+[guid]::NewGuid().ToString('N')+'.ps1'); [IO.File]::WriteAllText($p,$s,(New-Object Text.UTF8Encoding($false))); & "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NoProfile -ExecutionPolicy Bypass -File $p } catch { Write-Host $_ -ForegroundColor Red } }The collector automatically checks the normal installation locations plus locations found in relevant installer registrations. It records ancestor and installation-file ownership/permissions, raw security descriptors, reparse points, native file-open/security-query errors, MSI product/directory identities, Windows build, elevation, and reboot markers. Missing installation folders are recorded too. No separate screenshots, registry exports, or commands are needed for the initial collection.
It does not change permissions, uninstall anything, stop/start services, execute installed application code, or read configuration/credential file contents. It writes the diagnostic script, its text report, and temporary compiler files; it opens File Explorer at the end. It may take a few minutes for a large installation. The report contains paths/account SIDs, so review or redact identifying details before uploading it. The report is never uploaded automatically.
Validation so far: PowerShell syntax, embedded C# compilation, and mocked permission-classification checks passed. The native Windows probes still need an affected Windows machine; the collector records probe failures in the report. This gathers the evidence needed to distinguish an unsafe directory from a guard compatibility problem; it does not assume either outcome or guarantee a fix from one report.
Diagnostic source (automatically downloaded by the command above)
emreerisen commentedon Nov 1, 2026
I ran the read-only installer folder diagnostic from an elevated PowerShell session. The installer still fails with MSI error 1603 during
ValidateInstallRootPreview, with the message that the install folder or an existing product directory is unsafe, writable by an untrusted account, or contains a reparse point.The diagnostic results for this machine:
C:\Program Files\Apolloand its parent directories:GetSecurityInfoerror: 0HKLM\...\Vibepolloand points toC:\Program Files\Apollo\, with version2.0.2-daily.d20261014.1+66e169c325da.MsiGetProductInfoExreturns “This action is only valid for products that are currently installed”).So far, the diagnostic does not show an obvious permissions or reparse-point problem in the Apollo install directory. The stale-looking uninstall/MSI registration may be relevant, but I cannot confirm that it is the cause.
Could you please advise what to try next, or whether you can provide a more detailed/instrumented installer build that identifies exactly which path or validation check is causing
ValidateInstallRootPreviewto fail?I have the complete diagnostic report available and can attach/paste it if needed.
blitz2kx commentedon Nov 2, 2026
Same error - same issue. identical to what was posted.