Skip to content

Can't install due to owner-not-current-user error #1580

Closed

Description

@spam0115

Version

codebase-memory-mcp 0.9.0

Platform

Windows (x64)

Install channel

GitHub release archive / install.sh / install.ps1

Binary variant

standard

What happened, and what did you expect?

Tried to install version 10.3 on a Windows machine.

Note that this computer does not have Codex installed. I get an error saying that "error: failed to stage install candidate: activation transaction I/O failed: owner-not-current-user". I checked the "C:/Users/user/AppData/Local/Programs/codebase-memory-mcp" folder as well as the user temp folder and they are both owned by my account. I ran this as both admin and without admin and both produced the same error.

PS C:\Users\user> .\install.ps1
codebase-memory-mcp installer (Windows)
arch: amd64
target: C:\Users\user\AppData\Local\Programs\codebase-memory-mcp\codebase-memory-mcp.exe

Downloading codebase-memory-mcp-windows-amd64.zip...
Checksum verified.
Extracting...
Verified candidate: codebase-memory-mcp 0.10.3
codebase-memory-mcp install 0.10.3

Found 5 existing index(es). Keeping them. After install, re-index to pick up this version's improvements:
C:/Users/user/.cache/codebase-memory-mcp/C-s-test.db
C:/Users/user/.cache/codebase-memory-mcp/_config.db

error: failed to stage install candidate: activation transaction I/O failed: owner-not-current-user (os 0) at C:/Users/user/AppData/Local/Programs/codebase-memory-mcp
error: installation failed (exit code 1)

Reproduction

  1. install version 9.0
  2. try to install version 10.3

Logs


Diagnostics trajectory (memory / performance / leak issues)


Project scale (if relevant)

No response

Confirmations

  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.

Activity

  1. DeusData commented on Aug 13, 2026

    @DeusData
    Owner

    Thank you @spam0115 — root-caused, and your detail about checking the folder ownership is what made it findable.

    You were right that the folders are yours. The bug is on our side: we compared the file's owner against your user SID, but Windows stamps newly-created objects with the token's owner SID — and for a member of the Administrators group that defaults to BUILTIN\\Administrators. So the installer created its own staging file and then refused it, seconds later, as not belonging to you. Running as admin or not made no difference because both of your runs were in the Administrators group.

    Fix in PR #1583: we now also accept the SID Windows actually stamps on what we create. It is strictly narrower than "trust any administrator" — a file owned by a different account is still refused — and on a non-elevated account nothing changes at all.

    One honest caveat, and a request. Our Windows CI and test VM both run as ordinary users, where those two SIDs are identical — so they prove the fix compiles and breaks nothing, but they cannot exercise the elevated path that is actually failing for you. You are in a better position to verify this than we are. When the next release ships I will ping this issue; if you would re-run install.ps1 then and tell us what happens, that would confirm it properly.

    Also filed alongside yours: #1582, a fresh install from C:\\Users\\Administrator whose MCP server exits during startup — almost certainly the same cause from the other end.

  2. added
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    on Aug 13, 2026
  3. added a commit that references this issue on Aug 13, 2026
  4. DeusData commented on Aug 13, 2026

    @DeusData
    Owner

    Fixed on main — commit 0215e9b4 (PR #1583), shipping in the next release.

    The ownership check now also accepts the SID Windows actually stamps on objects we create (TokenOwner), not just your user SID (TokenUser). On an Administrators-group account those differ, which is why we were refusing files we had written seconds earlier.

    Verified on a real Windows VM (302 tests, 0 failures) plus full CI. As I mentioned, neither venue runs elevated, so the elevated path itself is the piece only you can confirm — I will ping here when the release is out.

  5. DeusData commented on Aug 14, 2026

    @DeusData
    Owner

    We said we would ping you when the next release shipped, and then didn't — apologies for the delay.

    v0.10.4 is out and contains the fix (0215e9b4, PR #1583).

    Your observation is what root-caused it. You wrote that the directories were "both owned by my account" and that running as admin and non-admin produced the identical error — that ruled out the obvious explanations and pointed at the comparison itself rather than the ownership.

    The bug: we compared the file's owner against the token's user SID, but Windows stamps a newly-created object with the token's owner SID, which for any member of the Administrators group defaults to BUILTIN\Administrators. So the installer created its own staging file and then refused it, seconds later, as belonging to someone else. Nothing was actually wrong with your machine.

    We now also accept the SID Windows actually stamps on what we create. It is narrower than "trust any administrator" — a file owned by a genuinely different account is still refused.

    One honest caveat: neither our CI nor our test VM runs as an elevated account, so the elevated path is not exercised in any venue we own. If v0.10.4 installs cleanly for you, that confirmation is genuinely valuable to us. If it still fails, the error text will have changed and that is useful too — please paste whatever it says.

    Thank you for the report and for the detail you put into it.

  6. spam0115 commented on Aug 14, 2026

    @spam0115
    Author

    btw, I'm not sure if it is of any use to you now but I created this install script to get around the issue. It's based off the 10.2 install script:

    #
    # Usage: see README.md for install instructions.
    #
    # Environment:
    #   CBM_DOWNLOAD_URL  Override base URL for downloads (for testing)
    
    $ErrorActionPreference = "Stop"
    
    # Enforce TLS 1.2+ (older PowerShell defaults to TLS 1.0 which GitHub rejects)
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13
    Add-Type -AssemblyName System.Net.Http
    
    $Repo = "DeusData/codebase-memory-mcp"
    $InstallDir = "$env:LOCALAPPDATA\Programs\codebase-memory-mcp"
    $BinName = "codebase-memory-mcp.exe"
    $WindowsArchiveNames = @(
        $BinName,
        "LICENSE",
        "install.ps1",
        "THIRD_PARTY_NOTICES.md"
    )
    $BaseUrl = if ($env:CBM_DOWNLOAD_URL) { $env:CBM_DOWNLOAD_URL } else { "https://github.com/$Repo/releases/latest/download" }
    
    try { $BaseUri = [Uri]$BaseUrl } catch { $BaseUri = $null }
    $AllowLoopbackHttp = (
        $BaseUri -and $BaseUri.IsAbsoluteUri -and
        $BaseUri.Scheme -eq "http" -and $BaseUri.IsLoopback -and
        [string]::IsNullOrEmpty($BaseUri.UserInfo)
    )
    if (-not $BaseUri -or -not $BaseUri.IsAbsoluteUri -or
        ($BaseUri.Scheme -ne "https" -and -not $AllowLoopbackHttp) -or
        -not [string]::IsNullOrEmpty($BaseUri.UserInfo)) {
        Write-Host "error: refusing non-HTTPS download URL: $BaseUrl" -ForegroundColor Red
        exit 1
    }
    
    function Invoke-CbmDownload {
        param([Parameter(Mandatory=$true)][string]$Url,
              [Parameter(Mandatory=$true)][string]$OutFile)
    
        $current = [Uri]$Url
        $handler = New-Object System.Net.Http.HttpClientHandler
        $handler.AllowAutoRedirect = $false
        $client = New-Object -TypeName System.Net.Http.HttpClient -ArgumentList $handler
        $client.Timeout = [TimeSpan]::FromMinutes(10)
        try {
            for ($redirects = 0; $redirects -le 5; $redirects++) {
                $allowed = $current.IsAbsoluteUri -and
                    [string]::IsNullOrEmpty($current.UserInfo) -and
                    ($current.Scheme -eq "https" -or
                     ($AllowLoopbackHttp -and $current.Scheme -eq "http" -and
                      $current.IsLoopback))
                if (-not $allowed) {
                    throw "download redirect escaped the allowed transport: $current"
                }
                $response = $client.GetAsync(
                    $current, [System.Net.Http.HttpCompletionOption]::ResponseHeadersRead
                ).GetAwaiter().GetResult()
                try {
                    $status = [int]$response.StatusCode
                    if ($status -in @(301, 302, 303, 307, 308)) {
                        if ($redirects -eq 5 -or -not $response.Headers.Location) {
                            throw "invalid or excessive download redirect from $current"
                        }
                        $current = [Uri]::new($current, $response.Headers.Location)
                        continue
                    }
                    if (-not $response.IsSuccessStatusCode) {
                        throw "HTTP $status for $current"
                    }
                    $input = $response.Content.ReadAsStreamAsync().GetAwaiter().GetResult()
                    try {
                        $output = [System.IO.File]::Open(
                            $OutFile, [System.IO.FileMode]::Create,
                            [System.IO.FileAccess]::Write,
                            [System.IO.FileShare]::None)
                        try { $input.CopyTo($output) } finally { $output.Dispose() }
                    } finally { $input.Dispose() }
                    return
                } finally { $response.Dispose() }
            }
            throw "too many download redirects"
        } finally {
            $client.Dispose()
            $handler.Dispose()
        }
    }
    
    function Protect-CbmInstallStage {
        param([Parameter(Mandatory=$true)][string]$Path)
    
        $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
        & icacls $Path /setowner $currentUser | Out-Null
        if ($LASTEXITCODE -ne 0) {
            throw "could not set install staging directory owner"
        }
        & icacls $Path /inheritance:r /grant:r "${currentUser}:(OI)(CI)(F)" "SYSTEM:(OI)(CI)(F)" "Administrators:(OI)(CI)(F)" | Out-Null
        if ($LASTEXITCODE -ne 0) {
            throw "could not harden install staging directory ACLs"
        }
    }
    
    $SkipConfig = $false
    foreach ($arg in $args) {
        if ($arg -eq "--skip-config") { $SkipConfig = $true }
        if ($arg -like "--dir=*") { $InstallDir = $arg.Substring(6) }
    }
    
    # Detect the OS architecture. RuntimeInformation.OSArchitecture reports the real
    # OS arch (Arm64) even from an x64 process running under emulation on ARM64 --
    # unlike $env:PROCESSOR_ARCHITECTURE, which reports the emulated "AMD64", and
    # PROCESSOR_ARCHITEW6432, which is unset for 64-bit emulated processes. Fall back
    # to the env vars only if the .NET API is somehow unavailable.
    if ($env:CBM_ARCH) {
        # Explicit override wins - used by CI/tests, and an escape hatch under x64
        # emulation on ARM64 where no in-process detection is reliable.
        $Arch = $env:CBM_ARCH
    } else {
        try {
            $osArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
            $Arch = if ($osArch -eq 'Arm64') { "arm64" } else { "amd64" }
        } catch {
            if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64" -or $env:PROCESSOR_ARCHITEW6432 -eq "ARM64") {
                $Arch = "arm64"
            } else {
                $Arch = "amd64"
            }
        }
    }
    
    Write-Host "codebase-memory-mcp installer (Windows)"
    Write-Host "  arch:    $Arch"
    Write-Host "  target:  $InstallDir\$BinName"
    Write-Host ""
    
    $Dest = Join-Path $InstallDir $BinName
    $Cwd = (Get-Location).ProviderPath
    $UseLocalBundle = $false
    if ($Cwd) {
        $missingLocalEntries = @($WindowsArchiveNames | Where-Object {
            -not (Test-Path -LiteralPath (Join-Path $Cwd $_) -PathType Leaf)
        })
        if ($missingLocalEntries.Count -eq 0) {
            $localBinary = Join-Path $Cwd $BinName
            if ([System.IO.Path]::GetFullPath($localBinary) -ne [System.IO.Path]::GetFullPath($Dest)) {
                $UseLocalBundle = $true
            }
        }
    }
    
    # Build download URL
    $Archive = "codebase-memory-mcp-windows-$Arch.zip"
    $Url = "$BaseUrl/$Archive"
    
    $TmpDir = $null
    if ($UseLocalBundle) {
        Write-Host "Using local release bundle from $Cwd"
        $TmpDir = Join-Path $env:LOCALAPPDATA "Temp\cbm-install-local-$(Get-Random)"
        try {
            New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null
            Protect-CbmInstallStage -Path $TmpDir
            foreach ($archiveName in $WindowsArchiveNames) {
                Copy-Item -LiteralPath (Join-Path $Cwd $archiveName) -Destination (Join-Path $TmpDir $archiveName) -Force -ErrorAction Stop
            }
        } catch {
            Write-Host "error: could not stage local release bundle: $_" -ForegroundColor Red
            Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue
            exit 1
        }
        $DownloadedBinary = Join-Path $TmpDir $BinName
        $DownloadedInstaller = Join-Path $TmpDir "install.ps1"
    } else {
        # Download
        $TmpDir = Join-Path ([System.IO.Path]::GetTempPath()) "cbm-install-$(Get-Random)"
        New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null
    
        Write-Host "Downloading $Archive..."
        try {
            Invoke-CbmDownload -Url $Url -OutFile "$TmpDir\$Archive"
        } catch {
            Write-Host "error: download failed: $_" -ForegroundColor Red
            Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue
            exit 1
        }
    
    
        # Checksum verification is mandatory. Do not request coordinated shutdown for
        # a candidate that was not positively matched to the published release digest.
        $ChecksumUrl = "$BaseUrl/checksums.txt"
        try {
            Invoke-CbmDownload -Url $ChecksumUrl -OutFile "$TmpDir\checksums.txt"
            $checksumPath = "$TmpDir\checksums.txt"
            if ((Get-Item -LiteralPath $checksumPath).Length -gt 1048576) {
                throw "checksums.txt exceeds the 1 MiB safety limit"
            }
            $checksumLines = @(Get-Content -LiteralPath $checksumPath | Where-Object {
                $parts = $_ -split '\s+'
                $parts.Count -ge 2 -and $parts[1].TrimStart('*') -eq $Archive
            })
            if ($checksumLines.Count -eq 0) {
                throw "no digest for $Archive in checksums.txt"
            }
            $expected = $null
            foreach ($checksumLine in $checksumLines) {
                $digest = (($checksumLine -split '\s+')[0]).ToLower()
                if ($digest -notmatch '^[0-9a-f]{64}$') {
                    throw "invalid SHA-256 digest for $Archive"
                }
                if ($null -ne $expected -and $expected -ne $digest) {
                    throw "conflicting SHA-256 digests for $Archive"
                }
                $expected = $digest
            }
            $actual = (Get-FileHash -Path "$TmpDir\$Archive" -Algorithm SHA256).Hash.ToLower()
            if ($expected -ne $actual) {
                throw "CHECKSUM MISMATCH (expected $expected, actual $actual)"
            }
            Write-Host "Checksum verified."
        } catch {
            Write-Host "error: checksum verification failed: $_" -ForegroundColor Red
            Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue
            exit 1
        }
    
        # Validate the zip namespace before extraction. Windows paths are
        # case-insensitive, so two entries that differ only in case are ambiguous and
        # must never be allowed to overwrite each other. The official five entries are
        # required at the archive root with their exact release names.
        try {
            Add-Type -AssemblyName System.IO.Compression.FileSystem
            $zip = [System.IO.Compression.ZipFile]::OpenRead("$TmpDir\$Archive")
            try {
                $seen = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)
                $archiveCounts = @{}
                foreach ($archiveName in $WindowsArchiveNames) { $archiveCounts[$archiveName] = 0 }
                foreach ($entry in $zip.Entries) {
                    $entryName = $entry.FullName.Replace('\', '/')
                    $isDirectory = $entryName.EndsWith('/')
                    $pathForSegments = if ($isDirectory) { $entryName.TrimEnd('/') } else { $entryName }
                    $segments = @($pathForSegments.Split('/'))
                    if ([string]::IsNullOrEmpty($pathForSegments) -or
                        $entryName.StartsWith('/') -or
                        $entryName.Contains(':') -or
                        $segments -contains '' -or
                        $segments -contains '.' -or
                        $segments -contains '..' -or
                        @($segments | Where-Object { $_.EndsWith('.') -or $_.EndsWith(' ') }).Count -gt 0) {
                        throw "unsafe zip entry path: $($entry.FullName)"
                    }
                    if (-not $seen.Add($pathForSegments)) {
                        throw "duplicate or case-conflicting zip entry: $($entry.FullName)"
                    }
                    if (-not ($WindowsArchiveNames -ccontains $entryName) -or $isDirectory) {
                        throw "archive contains an unexpected root entry: $($entry.FullName)"
                    }
                    $archiveCounts[$entryName] = $archiveCounts[$entryName] + 1
                }
                foreach ($archiveName in $WindowsArchiveNames) {
                    if ($archiveCounts[$archiveName] -ne 1) {
                        throw "archive must contain exactly one $archiveName"
                    }
                }
                if ($seen.Count -ne $WindowsArchiveNames.Count) {
                    throw "archive does not match the exact Windows release allowlist"
                }
            } finally {
                $zip.Dispose()
            }
        } catch {
            Write-Host "error: unsafe or incomplete release archive: $_" -ForegroundColor Red
            Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue
            exit 1
        }
    
        # Extract the validated bundle. Windows ships ONE binary, exactly like Linux
        # and macOS; this script is what replaces it, because a running executable
        # cannot replace itself on Windows. Re-running this script IS the update.
        Write-Host "Extracting..."
        Expand-Archive -Path "$TmpDir\$Archive" -DestinationPath $TmpDir -Force
    
        $DownloadedBinary = Join-Path $TmpDir $BinName
        $DownloadedInstaller = Join-Path $TmpDir "install.ps1"
    }
    if (-not (Test-Path -LiteralPath $DownloadedBinary -PathType Leaf)) {
        Write-Host "error: $BinName not found after extraction" -ForegroundColor Red
        if ($TmpDir) { Remove-Item -Recurse -Force $TmpDir }
        exit 1
    }
    $binaryItem = Get-Item -LiteralPath $DownloadedBinary
    if ($binaryItem.Attributes -band [System.IO.FileAttributes]::ReparsePoint) {
        Write-Host "error: refusing reparse-point executable in release archive" -ForegroundColor Red
        if ($TmpDir) { Remove-Item -Recurse -Force $TmpDir }
        exit 1
    }
    
    # Prove the downloaded binary runs before touching an existing installation.
    try {
        $candidateVersion = & $DownloadedBinary --version 2>&1
        if ($LASTEXITCODE -ne 0) { throw "candidate exited with $LASTEXITCODE" }
        Write-Host "Verified candidate: $candidateVersion"
    } catch {
        Write-Host "error: downloaded binary failed to run: $_" -ForegroundColor Red
        if ($TmpDir) { Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue }
        exit 1
    }
    
    try {
        New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
        Protect-CbmInstallStage -Path $InstallDir
    } catch {
        Write-Host "error: could not prepare install directory ACLs: $_" -ForegroundColor Red
        if ($TmpDir) { Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue }
        exit 1
    }
    
    # Retire the running installation before replacing it. Windows keeps an image
    # lock on a running .exe: the file cannot be overwritten, but it CAN be renamed
    # out of the way, which is what makes an in-place update possible from here.
    if (Test-Path -LiteralPath $Dest -PathType Leaf) {
        try { & $Dest daemon stop 2>&1 | Out-Null } catch { }
        $retired = "$Dest.retired-$(Get-Date -Format yyyyMMddHHmmss)"
        $renamed = $false
        foreach ($attempt in 1..10) {
            try { Move-Item -LiteralPath $Dest -Destination $retired -Force -ErrorAction Stop; $renamed = $true; break }
            catch { Start-Sleep -Milliseconds 500 }
        }
        if (-not $renamed) {
            Write-Host "error: could not retire the existing $BinName - close all running" -ForegroundColor Red
            Write-Host "       codebase-memory-mcp sessions and coding agents, then re-run." -ForegroundColor Red
            if ($TmpDir) { Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue }
            exit 1
        }
        # A retired image stays locked until its last process exits; delete it when
        # we can, and leave it for the next run when we cannot. Never fail here.
        Remove-Item -LiteralPath $retired -Force -ErrorAction SilentlyContinue
    }
    Get-ChildItem -LiteralPath $InstallDir -Filter "$BinName.retired-*" -ErrorAction SilentlyContinue |
        ForEach-Object { Remove-Item -LiteralPath $_.FullName -Force -ErrorAction SilentlyContinue }
    
    $InstallArgs = @("install", "-y", "--force", "--dir=$InstallDir")
    if ($SkipConfig) { $InstallArgs += "--skip-config" }
    $ActivationTmpRoot = Join-Path $env:LOCALAPPDATA "codebase-memory-mcp-install-tmp-$(Get-Random)"
    $previousTemp = $env:TEMP
    $previousTmp = $env:TMP
    try {
        New-Item -ItemType Directory -Path $ActivationTmpRoot -Force | Out-Null
        Protect-CbmInstallStage -Path $ActivationTmpRoot
        $env:TEMP = $ActivationTmpRoot
        $env:TMP = $ActivationTmpRoot
        & $DownloadedBinary @InstallArgs
    } finally {
        $env:TEMP = $previousTemp
        $env:TMP = $previousTmp
        Remove-Item -Recurse -Force $ActivationTmpRoot -ErrorAction SilentlyContinue
    }
    if ($LASTEXITCODE -ne 0) {
        Write-Host "error: installation failed (exit code $LASTEXITCODE)" -ForegroundColor Red
        if ($TmpDir) { Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue }
        exit 1
    }
    
    # Place the installer beside the binary so `update` points at a local file
    # rather than a URL, and so the next update runs THIS release's installer.
    #
    # Sourced from the archive we just checksum-verified, and published by rename
    # rather than written over the live path. PowerShell parses a script fully
    # before executing it, so self-overwrite is less hazardous here than it is for
    # bash -- but rename costs nothing and keeps both platforms on one rule.
    # Best effort: a failure here still leaves a working install.
    if (Test-Path -LiteralPath $DownloadedInstaller -PathType Leaf) {
        $InstallerDest = Join-Path $InstallDir "install.ps1"
        $InstallerTmp = "$InstallerDest.new"
        try {
            Copy-Item -LiteralPath $DownloadedInstaller -Destination $InstallerTmp -Force -ErrorAction Stop
            Move-Item -LiteralPath $InstallerTmp -Destination $InstallerDest -Force -ErrorAction Stop
            Write-Host "Installed updater -> $InstallerDest"
        } catch {
            Remove-Item -LiteralPath $InstallerTmp -Force -ErrorAction SilentlyContinue
            Write-Host "note: could not place install.ps1 in $InstallDir (update will explain where to find it)"
        }
    }
    
    # Verify
    try {
        $ver = & $Dest --version 2>&1
        if ($LASTEXITCODE -ne 0) { throw "installed binary exited with $LASTEXITCODE" }
        Write-Host "Installed: $ver"
    } catch {
        Write-Host "error: installed binary failed to run" -ForegroundColor Red
        if ($TmpDir) { Remove-Item -Recurse -Force $TmpDir }
        exit 1
    }
    
    # Agent configuration was included in the candidate-owned activation window.
    if ($SkipConfig) {
        Write-Host ""
        Write-Host "Skipping agent configuration (--skip-config)"
    }
    
    # The verified candidate persisted the current-user PATH while holding the
    # coordinated activation lease. Do not perform a second registry mutation here
    # after running sessions have been allowed to restart.
    
    # Cleanup
    if ($TmpDir) { Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue }
    
    Write-Host ""
    Write-Host "Done! Restart your terminal and coding agent to start using codebase-memory-mcp."
    
  7. DeusData commented on Aug 14, 2026

    @DeusData
    Owner

    This is very much of use — you independently found the same fix we did, and your script is the better evidence of the two.

    The decisive lines are these:

    & icacls $Path /setowner $currentUser
    & icacls $Path /inheritance:r /grant:r "${currentUser}:(OI)(CI)(F)" "SYSTEM:(OI)(CI)(F)" "Administrators:(OI)(CI)(F)"

    /inheritance:r is exactly the operation install.ps1 was missing. Ours creates its staging directory with New-Item, which inherits whatever %TEMP% carries — and then runs the downloaded binary from that directory, where the binary validates its own directory and refuses inherited cross-account mutation grants. So the installer was handing our own validator a directory the installer had made wrong. That is the acl-grants-cross-account-mutation half; the owner-not-current-user half you originally reported is the TokenOwner bug, fixed separately in v0.10.4.

    I measured it on a Windows VM before writing the fix, and your workaround is doing the same thing:

    INHERITED-BEFORE: protected=False aces=5     <- fresh New-Item staging dir
    HARDENED-AFTER : protected=True  aces=1
    

    Five inherited ACEs on a directory we had just created ourselves.

    The fix is up as #1625 — it applies a protected owner-only DACL to the staging directory via Set-Acl, which is the same end state as your icacls /inheritance:r /grant:r. Two differences worth naming: I used Set-Acl rather than icacls because the CreateDirectory overload that takes a DirectorySecurity exists on PowerShell 5.1 but not on 7, and I grant only the current user rather than also SYSTEM and Administrators — both of those are already in our trusted set, so granting them is fine, it is just not necessary.

    It also means the TMP/TEMP redirection workaround circulating in #1529 becomes unnecessary.

    Two things I would still value from you, if you have the patience:

    1. Does v0.10.4 install cleanly now? Neither our CI nor our test VM runs as an elevated account, so the elevated path is unverified in every venue we own. Your confirmation is worth more than our test suite here.
    2. If it does not, the error text will have changed — please paste whatever it says.

    Thank you for going to the trouble of writing a working installer and then handing it back to us rather than just keeping it. That is a genuinely generous way to file a bug.

  8. spam0115 commented on Aug 14, 2026

    @spam0115
    Author

    "Does v0.10.4 install cleanly now?" It does install cleanly except for it's inability to update the codex/config.toml file. Even though it already had entries from the recent v0.10.2, it was unable to recognized the entries and reported an error. I had to delete all references to cbm in the toml file by hand.

    Detected agents: Codex VS-Code
    
    error: agent_config agent=Codex CLI op=hook_preflight path=C:/Users/zwong/.codex/config.toml
    VS Code:
      mcp: C:/Users/zwong/AppData/Roaming/Code/User/mcp.json
      skill: C:/Users/zwong/.copilot/skills/codebase-memory/SKILL.md (1 installed)
      agent: C:/Users/zwong/.copilot/agents/codebase-memory-scout.agent.md
      agent: C:/Users/zwong/.copilot/agents/codebase-memory.agent.md
      agent: C:/Users/zwong/.copilot/agents/codebase-memory-auditor.agent.md
      hooks: SessionStart + SubagentStart (dynamic graph context)
    error: one or more agent configurations failed; the published/current executable was kept, and PATH/index cleanup was not attempted
    error: activation stopped after one or more agent configuration or cleanup operations failed; the published/current executable was kept, and configuration changes that completed may remain. Please restart your coding-agent sessions after resolving the errors above.
    error: installation failed (exit code 1)
    PS C:\Users\zwong\Downloads> .\install.ps1
    codebase-memory-mcp installer (Windows)
      arch:    amd64
      target:  C:\Users\zwong\AppData\Local\Programs\codebase-memory-mcp\codebase-memory-mcp.exe
    
  9. DeusData commented on Aug 14, 2026

    @DeusData
    Owner

    Thank you — and that is a genuinely useful answer, because it confirms one thing and exposes another.

    Confirmed: the install itself is fixed. You were the only venue that could tell us; neither our CI nor our test VM runs as an elevated account, so the TokenOwner fix shipped in v0.10.4 unverified everywhere we control. Your "it does install cleanly" is the only real evidence it works.

    The Codex failure is a different bug, and your description names it precisely: "even though it already had entries from the recent v0.10.2, it was unable to recognize the entries." Having to hand-delete every cbm reference before we would proceed is not an acceptable upgrade path, and I am sorry you had to.

    That is the same shape as a bug fixed for OpenCode earlier today (#1630 / PR #1640): our ownership check required the entry to match what this version writes, exactly — so an entry written by an earlier version of cbm was classified as foreign and refused. Same defect, different config format: JSON there, TOML here. Your report is what shows it is a pattern rather than a one-off.

    Tracked in #1633, and @astandrik already has PR #1638 open adding real reasons to Codex preflight refusals — so the next occurrence will say which check refused instead of just failing. That PR plus your evidence should be enough to fix the recognition itself.

    One question if you still have it: did the old entries in config.toml differ from what v0.10.4 writes — extra keys, a different command path, a changed hook shape? Even a rough "it had X, the new one has Y" would let us fix the comparison rather than guess at it. Completely fine if you have already overwritten it.

    Genuinely appreciated — you reported the original bug, wrote a working installer to get around it, handed that back to us, and have now verified the fix and found the next one.

  10. DeusData commented on Aug 17, 2026

    @DeusData
    Owner

    Closing — both halves are now fixed. The owner-not-current-user staging failure was the TokenOwner fix shipped in v0.10.4, which you confirmed installs cleanly (and yours was the only elevated-account venue that could prove it — thank you). The residual you then hit — Codex hook_preflight refusing the entries v0.10.2 itself wrote — was tracked as #1633 and is fixed by #1638 (merge 3162f09, in v0.10.6): the reconciler now recognises the managed v0.10.2 SessionStart/SubagentStart hook pair (verified end-to-end against a byte-faithful v0.10.2-written config.toml, including the Windows CRLF+BOM variant) and rewrites it in place, so no more hand-deleting cbm references. If v0.10.6 still refuses your real config.toml, please reopen with the refusal line — it now names the reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingeditor/integrationEditor compatibility and CLI integrationpriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.windowsWindows-specific issues

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions