Skip to content

Interrupted versioned user update can lose product target and install system setup #327904

Description

@dmitrivMS

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:

  1. The recursive Source: "*" entry copies the generic build product.json, which has no target.
  2. 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

  1. Install and launch an Insiders User Setup build that uses versioned Windows updates.
  2. Keep the matching VSCodeUserSetup-x64-<version>-insider.exe available.
  3. 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)
  1. Re-run the matching User Setup package using the update-style arguments:
& .\VSCodeUserSetup-x64-<version>-insider.exe /verysilent /log /nocloseapplications '/mergetasks=runcode,!desktopicon,!quicklaunchicon'
  1. When setup reports that it cannot replace vscode-policy-watcher.node, choose Abort, then release the handle:
$stream.Dispose()
  1. 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

Metadata

Metadata

Labels

bugIssue identified by VS Code Team member as probable buginstall-updateVS Code installation and upgrade system issueswindowsVS Code on Windows issues

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions