Does this issue occur when all extensions are disabled?: N/A
- VS Code Version: 1.131.0-insider (user setup)
- OS Version: Windows 11 x64 10.0.26200
Summary
A versioned Windows User Setup contains two entries for resources/app/product.json:
- The recursive
Source: "*" entry copies the generic build product.json, which has no target.
- A later explicit entry copies the setup-generated
product.json containing "target": "user".
If setup aborts after the first entry but before the second, rollback can leave the generic file in an existing version directory. The next VS Code process has productService.target === undefined, so it requests the system update feed and uses the foreground system setup flow. This can silently turn a user installation into parallel user and system installations.
Steps to Reproduce
- Install and launch an Insiders User Setup build that uses versioned Windows updates.
- Keep the matching
VSCodeUserSetup-x64-<version>-insider.exe available.
- In PowerShell, exclusively hold a file that occurs after
product.json in the recursive setup file list:
$installDir = "$env:LOCALAPPDATA\Programs\Microsoft VS Code Insiders"
$versionDir = Get-ChildItem $installDir -Directory |
Where-Object Name -Match '^[0-9a-f]{10}$' |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
$lockedFile = Join-Path $versionDir.FullName 'resources\app\node_modules.asar.unpacked\@vscode\policy-watcher\build\Release\vscode-policy-watcher.node'
$stream = [IO.File]::Open($lockedFile, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::Read)
- Re-run the matching User Setup package using the update-style arguments:
& .\VSCodeUserSetup-x64-<version>-insider.exe /verysilent /log /nocloseapplications '/mergetasks=runcode,!desktopicon,!quicklaunchicon'
- When setup reports that it cannot replace
vscode-policy-watcher.node, choose Abort, then release the handle:
- Inspect the installed product metadata:
$product = Get-Content (Join-Path $versionDir.FullName 'resources\app\product.json') -Raw | ConvertFrom-Json
$product.target
Actual Result
target is missing. On the next available update:
- The cache directory is named
vscode-insider-undefined-x64.
- Update metadata points to
VSCodeSetup-x64-...exe, not VSCodeUserSetup-x64-...exe.
- Setup runs with
/silent and creates an HKLM installation under C:\Program Files\Microsoft VS Code Insiders alongside the existing user installation.
The setup log for the aborted update contains one generic product.json write before the locked-file error. A successful setup normally contains a second product.json write with the target-specific generated file.
Expected Result
A User Setup package must never install a generic product.json. Its only product.json payload should always contain "target": "user", including after cancellation, failure, or rollback.
Root Cause
For versioned builds, the source file is located at:
<commit-prefix>\resources\app\product.json
The wildcard entry in build/win32/code.iss excludes only:
\resources\app\product.json
Therefore the versioned generic file is included by the wildcard. The setup-generated target-specific file is installed by a separate explicit entry later in the [Files] section.
This also explains why the update cache contains undefined: Win32UpdateService.cachePath interpolates productService.target, while feed selection treats only target === 'user' as a user setup.
Related: #279149, #111107, #120515, #304999
Does this issue occur when all extensions are disabled?: N/A
Summary
A versioned Windows User Setup contains two entries for
resources/app/product.json:Source: "*"entry copies the generic buildproduct.json, which has notarget.product.jsoncontaining"target": "user".If setup aborts after the first entry but before the second, rollback can leave the generic file in an existing version directory. The next VS Code process has
productService.target === undefined, so it requests the system update feed and uses the foreground system setup flow. This can silently turn a user installation into parallel user and system installations.Steps to Reproduce
VSCodeUserSetup-x64-<version>-insider.exeavailable.product.jsonin the recursive setup file list:vscode-policy-watcher.node, choose Abort, then release the handle:$stream.Dispose()Actual Result
targetis missing. On the next available update:vscode-insider-undefined-x64.VSCodeSetup-x64-...exe, notVSCodeUserSetup-x64-...exe./silentand creates an HKLM installation underC:\Program Files\Microsoft VS Code Insidersalongside the existing user installation.The setup log for the aborted update contains one generic
product.jsonwrite before the locked-file error. A successful setup normally contains a secondproduct.jsonwrite with the target-specific generated file.Expected Result
A User Setup package must never install a generic
product.json. Its onlyproduct.jsonpayload should always contain"target": "user", including after cancellation, failure, or rollback.Root Cause
For versioned builds, the source file is located at:
The wildcard entry in
build/win32/code.issexcludes only:Therefore the versioned generic file is included by the wildcard. The setup-generated target-specific file is installed by a separate explicit entry later in the
[Files]section.This also explains why the update cache contains
undefined:Win32UpdateService.cachePathinterpolatesproductService.target, while feed selection treats onlytarget === 'user'as a user setup.Related: #279149, #111107, #120515, #304999