Repository navigation
Read winget's Own Log When an Install Fails So a Portable Package Names Its Holders - #3851
Conversation
…es Its Holders A failed winget install or upgrade named the processes holding the tool's files only where the package's installer log or winget's exit code said files were in use. A portable or zip package has no installer, so it writes no such log, and winget reports its refusal to delete a running executable under a generic install-failed code. The upgrade then failed with no hint of what held the file. Get-InstallerFailureReason now also reads winget's own session log for the failed call, the latest one started at or after the attempt that names the package id, and keeps its error lines. An access denial or a sharing violation now counts as files in use in either log, matching the exit codes already read that way. Only processes running from the tool's own install are ever named, so a denial over permissions blames nothing unrelated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the Two Headers An installer log saying "Access is denied" can be a permission failure, which no running process causes, so only winget's own log now reads a denial or a sharing violation as files in use. The headers of Get-InstallerFailureReason and Write-InstallFailure said only the installer log gives a reason, which this change made false. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Write-Time Filter The header said a sharing violation counts as files in use only in winget's own log, but the installer-log wording already reads Windows' "used by another process" that way. Only a denial is limited to winget's log. A log created at or after the attempt was also written after it, so the write-time clause filtered nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
|
A recorded local strict-review pass covers head |
…it Example winget's own log counted any "Access is denied" error line as files in use, while the header justifies that only for winget failing to replace a file. Only a denial naming a file now counts. A sharing violation still does, through the "used by another process" wording. The Write-InstallFailure header also said a failed Git download leaves no log, which winget's own session log made false. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Active issues remain: the documentation is inaccurate about which logs may be absent, and broad access-denied matching can misclassify unrelated WinGet failures and trigger misleading process enumeration.
2 open issues
What changed in this PR
This pull request improves failed winget install diagnostics by reading winget’s own session logs, allowing portable packages to report file-use failures and likely holding processes.
Changes:
- Reads matching
WinGet-*.logfiles and extracts relevant error entries. - Shares failure-entry selection between winget and installer logs.
- Distinguishes winget file-removal failures from installer permission errors.
- Expands test coverage for portable packages, log selection, denial handling, and failure reporting.
The pull request requires changes to narrow access-denied matching to file-removal diagnostics and correct the stale comment about download failures leaving no log. Package IDs containing hyphens also require corrected boundary matching.
| File | Description |
|---|---|
tests/test_install_tools.py |
Adds coverage for winget-log parsing, portable packages, log selection, denial handling, and failure reports. |
host-setup/windows/install-tools.ps1 |
Adds winget session-log diagnostics, shared failure-entry selection, and holder reporting. |
Review effort: Lite
Neither pattern either caller passes can match an empty string or a null, so the match already drops every entry the guard did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #3851 +/- ##
========================================
Coverage 83.30% 83.30%
========================================
Files 29 29
Lines 9937 9937
========================================
Hits 8278 8278
Misses 1659 1659
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
A recorded local strict-review pass covers head |
…#3856) Promotes the changes merged into `develop` since promotion #3849 to `main`. - [#3851](#3851) reads winget's own log for a failed install or upgrade in `host-setup/windows/install-tools.ps1`, so a portable package whose files are in use names the processes holding them. - [#3855](#3855) runs each `claude` child in `scripts/skills_install.py` through the path `shutil.which` returned, so an npm `claude.cmd` shim is reached. It also makes a report child's deadline hold on Windows, where a 3-second deadline over a stalled git took 20.1 seconds and now takes 3.2. Measured on native Windows 11, with the full suite passing there. - [#3857](#3857) answers this promotion's first review round. A locked winget log no longer aborts the failure report, a package id in winget's own log matches whole, and an undecodable `claude` listing reads as unknown. Fixes #1754 🤖 Generated with [Claude Code](https://claude.com/claude-code)


Summary
A failed winget install or upgrade in
host-setup/windows/install-tools.ps1names the processes holding the tool's files only where something says files were in use. Before this change the only signals were the package's own installer log (<id>.*.log) and a short list of exit codes. A portable or zip package,astral-sh.uvamong them, has no installer, so it writes no such log. winget's refusal to delete a running executable is reported under the generic0x8A150052, so such an upgrade failed with no hint of what held the file.Get-InstallerFailureReasonnow also reads winget's ownWinGet-<timestamp>.logfor the failed call. That is the latest one created at or after the attempt that names the package id, which winget logs among its arguments, and only its<E>lines are kept. Thelistandshowprobes that run before the attempt are passed over.Access is denied.: "<path>") counts as files in use, as does Windows' "used by another process". A denial naming no file does not. The installer-log wording is unchanged, since an installer's "Access is denied" can be a permission failure no running process causes.Select-FailureEntryis the old in-function "last few entries, keep the in-use one" logic pulled out, so both logs share it.Write-InstallFailureprints awinget's own log says:block and the log's path beside the installer-log block.This applies to every winget package rather than to uv alone. A package with an installer still reports through its installer log as before.
The
commentslabel is on this pull request because the change corrects the header comments of both functions, which the new behavior would otherwise leave false.Verification
uv run --no-project --with pytest python -m pytest tests/test_install_tools.py -q: 23 passed, 18 skipped. The new cases cover a portable package's reason read from winget's log, a longer id that only starts with the package id not being read as its log, an installer log's denial alone and a winget denial naming no file not counting as in use, and the report naming holders from winget's log.Get-InstallerFailureReasonrun on a Windows 11 host against a real failed portable upgrade's DiagOutputDir selected the upgrade's own session log over the probes before it, setInUseto true, and returned theremove: Access is denied.entry.Findings from the local strict review
Five recorded passes ran, the last over
10a272bawith no findings. The twointroducedfindings this body listed after the opening push are both fixed in8e5a0b6b:host-setup/windows/install-tools.ps1:601(introduced): theWrite-InstallFailureheader said a Git upgrade that failed on a download "leaves no log", which winget's own session log made false. Fixed in8e5a0b6b.host-setup/windows/install-tools.ps1:507(introduced): the winget-log in-use pattern accepted "access is denied" on any<E>line. Fixed in8e5a0b6b, which counts only a denial naming a file.The fourth pass also raised a dead empty-entry guard in
Select-FailureEntry, fixed in10a272ba. It raised one more, a denial naming a file outside the tool's install tree, such as winget's temp download folder under an antivirus lock, counting as in use. That one is declined underGOVERNANCE.md"Trust Boundaries and Hardening Effort". Its only effect is a printed suggestion naming processes running from the tool's own install tree, which is neither silent nor destructive nor one an outsider can trigger. winget logging such a denial is also unverified.🤖 Generated with Claude Code