This was investigated following .github/triage/PLAYBOOK.md and structured per the via-triage template, but the via-triage label was dropped on creation since I don't have permission to apply labels. Could a maintainer add it? Thanks!
Windows in-app update silently fails when the installer signature check exceeds electron-updater's 20s PowerShell timeout#20345
What happened
On a corporate-managed Windows 11 machine, the in-app update never completes. Clicking download shows progress up to 100%, then the update quietly goes nowhere. No visible error appears. The same nightly updates fine on a personal Windows machine.
Diagnosis
The download itself succeeds. The failure is electron-updater's Windows signature check, which runs after the download and before the temp file is renamed into place.
NsisUpdatercallsverifySignature()fromwindowsExecutableCodeSignatureVerifier.js. That function spawnspowershell.exe ... Get-AuthenticodeSignature -LiteralPath '<temp file>' | ConvertTo-Json -CompressthroughexecFilewith a hardcodedtimeout: 20 * 1000.- On machines with endpoint security that scans a newly written ~240 MB executable on first open (and/or inspects PowerShell launches), that first
Get-AuthenticodeSignaturecall can take longer than 20 s. Node kills the child, and the rejection isError: Command failed: <command>with empty stderr. - The failure was logged 27.1 s after
download progress: 100. That gap is consistent with SHA-512 verification plus the 20 s kill. - After the failure, I re-ran the exact same command by hand against the same file. It completed in 4.2 s with
Status: 0(Valid) and signerCN=T3 Tools Inc. So the file and signature are fine; only the first, scan-blocked invocation is slow. PowerShell runs inFullLanguagemode on this machine, so the check is not blocked by policy. - The leftover
pending/temp-T3-Code-0.1.1-nightly.20261016.3218-x64.exeis exactly 242,348,152 bytes, and its SHA-512 matchesnightly.ymlfor that release.
T3 Code does not override verifyUpdateCodeSignature (apps/desktop/src/electron/ElectronUpdater.ts), so it inherits the 20 s limit. describeUpdateErrorCause keeps only message and string code. That drops killed: true / signal: "SIGTERM", so the trace can't tell a timeout apart from a real signature failure.
Steps to reproduce
Depends on the endpoint-security stack, so it is not deterministic everywhere:
- On a Windows machine with real-time/EDR scanning of newly written executables under
%LOCALAPPDATA%, run a nightly build. - Open the update flyout and download the available update.
- Progress reaches 100%, then the update does not become ready to install.
desktop.trace.ndjsonshowsElectronUpdaterDownloadUpdateErrorwith theGet-AuthenticodeSignaturecommand as the cause. %LOCALAPPDATA%\t3code-updater\pending\keeps a full-sizetemp-*.exethat never gets promoted.
Without that kind of AV, a unit test can stand in: make the injected verifier reject the way execFile does on timeout (killed: true, empty stderr).
Version
0.0.46-nightly.20261015.3145, updating to 0.1.1-nightly.20261016.3218. electron-updater ^6.8.9 per apps/desktop/package.json on main.
Environment
Windows 11 Enterprise 10.0.26100 x64, corporate-managed (endpoint security, no admin rights), desktop app per-user NSIS install, nightly channel.
Evidence
| 1 | # desktop.trace.ndjson (home path redacted) |
| 2 | desktop.updates.applyDownloadProgress download progress {"percent":100} t=…666.31s |
| 3 | desktop.updates.handleUpdaterError "Desktop updater download operation reported an error." t=…693.45s (+27.1s) |
| 4 | errorTag: DesktopUpdaterReportedError, operation: download |
| 5 | cause: Error: Command failed: set "PSModulePath=" & chcp 65001 >NUL & powershell.exe -NoProfile -NonInteractive -InputFormat None -Command "Get-AuthenticodeSignature -LiteralPath '~\AppData\Local\t3code-updater\pending\temp-T3-Code-0.1.1-nightly.20261016.3218-x64.exe' | ConvertTo-Json -Compress" |
| 6 | desktop.updates.handleDownloadFailure "Electron updater failed to download the update on channel nightly." |
| 7 | errorTag: ElectronUpdaterDownloadUpdateError (same cause, no stderr) |
| 8 | |
| 9 | # Same command re-run manually minutes later against the same file: |
| 10 | exit=0 elapsed=4.2s "Status":0 signer CN=T3 Tools Inc |
| 11 | |
| 12 | # Leftover file vs. published nightly.yml |
| 13 | pending/temp-T3-Code-0.1.1-nightly.20261016.3218-x64.exe size=242348152 sha512 matches nightly.yml |
Related issues
- #6035: same
failed to download the updatestring on Windows, but it failed in ~200 ms (before any download), so it's a different cause. - #11708: same string, but the reporter traced it to a failing SSD.
- #4812: same string, but on macOS behind a TLS-inspecting proxy, stalling at 0%.
I found no existing electron-builder issue about this timeout.
Fix applied or workaround
Nothing changed on the machine. Workaround: close T3 Code and run the leftover pending\temp-*.exe (after checking its SHA-512 against nightly.yml), or download the installer from the release page. Neither needs admin rights.
Suggested fixes
NsisUpdater already exposes a setter for this check: autoUpdater.verifyUpdateCodeSignature = (publisherNames, path) => Promise<string | null>. electron-updater's own docs suggest it for custom verification. Options, smallest first:
- Retry the default verifier on timeout (Windows only). Capture the original
autoUpdater.verifyUpdateCodeSignature, wrap it, and retry once or twice when the rejection is a killed child process (error.killed === true). The second run is fast because the AV scan of the file is already done and cached (4.2 s here). Publisher matching and the security posture stay exactly the same. The logic is a few lines inElectronUpdater.ts, plus a unit test for timeout → retry → success, and for a real mismatch still failing. - Avoid spawning PowerShell entirely. Verify with
WinVerifyTrustin-process, for example viawin-verify-trust, which the electron-updater docs mention. This removes the 20 s limit, the PowerShell process launch that endpoint security tends to inspect, and thePSModulePath/chcpworkarounds. - Make timeouts diagnosable. Have
describeUpdateErrorCauseincludekilled/signal(and numeric exit codes) for child-process errors, or tag signature-verification failures distinctly, soCommand failed:with empty stderr shows up as a timeout. - Surface the failure. The user saw no error. A download failure with a short hint ("Signature verification timed out — security software may be scanning the installer. Retry?") would make this self-service.
A configurable timeout in electron-updater itself would also help, but options 1 or 2 are fully within T3 Code's control.
Filed by
Claude Code (claude-opus-5-5), investigating by hand following .github/triage/PLAYBOOK.md, not via npx t3 triage.
Note
Grok responding on behalf of Julius.
Accepting this as a bug in how T3 handles electron-updater's Windows signature check. The 20 s timeout is electron-updater's, but T3 can guard against it.
What main does today:
apps/desktop/src/electron/ElectronUpdater.tsnever overridesautoUpdater.verifyUpdateCodeSignature, so a download inherits electron-updater's built-in PowerShellGet-AuthenticodeSignaturecheck and its time limit (electron-updateris^6.8.9inapps/desktop/package.json).describeUpdateErrorCauseinapps/desktop/src/updates/DesktopUpdates.tskeeps only the error's tag, stringcodeand the first line ofmessage. It dropskilledandsignal, so in the trace a timed-out check looks the same as a genuine signature failure.
Workaround for now: close T3 Code and download the installer from the releases page. Or, after checking the leftover pending\temp-*.exe against the SHA-512 in the release's .yml, run that file.
The fix direction (retrying the check after a timeout or verifying in-process, and reporting timeouts clearly) is still open.
Answers to the triage questions (the triage comment appears to have been removed, but it reached my inbox), plus a test of the reverted retry and uncapped timings.
Current nightly: yes. Reproduced on 2026-10-22 updating 0.1.1-nightly.20261020.3411 (d36b7da, after revert 8fc72da) → 0.1.2-nightly.20261022.3512. No error is shown in the app; the update just never becomes ready. Every update since filing has needed a manual install.
| 1 | 07:45:50.0 download progress 100% (243,760,776 bytes) |
| 2 | 07:45:50.5 powershell.exe Get-AuthenticodeSignature … temp-T3-Code-0.1.2-nightly.20261022.3512-x64.exe starts |
| 3 | 07:46:18.2 handleUpdaterError + handleDownloadFailure: "Command failed: … Get-AuthenticodeSignature …" (no stderr) |
| 4 | 07:46:44 that powershell.exe exits on its own: ~53.5 s wall, ~4.4 s CPU |
Retry test (#21273). 0.1.1-nightly.20261020.3401 is the only published build with the reverted retry. Installed it and updated to 3512 from it:
| 1 | 08:04:15.1 download progress 100% |
| 2 | 08:04:15.4 check 1 powershell starts |
| 3 | 08:04:37.4 check 2 (retry) starts; check 1 still running |
| 4 | 08:05:04.2 handleDownloadFailure (same "Command failed" cause); a failure popup was shown on this build |
| 5 | 08:05:06.4 check 1 exits: ~50.9 s wall, ~1.9 s CPU |
| 6 | 08:05:16.0 check 2 exits: ~38.6 s wall, ~2.6 s CPU |
The retry fires, but one immediate retry does not help here. The 20 s timeout kills cmd.exe (shell: true), not powershell.exe, so the retry runs while the first check is still blocked, and it times out too.
Uncapped check timing. To check whether a longer limit would pass, I downloaded fresh copies of the 3512 installer three times and immediately ran the identical cmd.exe … powershell.exe Get-AuthenticodeSignature … | ConvertTo-Json -Compress command with no timeout:
| Trial | Check duration | Result |
|---|---|---|
| 1 | 55.8 s | Status 0, CN=T3 Tools Inc, no stderr |
| 2 | 56.0 s | same |
| 3 | 54.4 s | same |
Across all five observations on this machine (two updater attempts, three uncapped trials), the first check on a freshly downloaded installer takes 51–56 s and then succeeds. Once the file has been scanned, the same command takes ~3.6 s. These copies were downloaded by PowerShell into a folder next to the updater cache rather than by the app itself, but the timings match the updater runs. I don't know how this varies on other machines or security products, so I'm not suggesting a specific limit. On this machine, anything under ~60 s fails.
Security product: A third-party enterprise endpoint-security suite with on-access scanning. Windows Defender is registered but passive.
Scan/block event: not available. This is a managed machine without admin rights, the product's current logs aren't readable, and the Defender log has nothing in that window. What separates a timeout from another PowerShell failure:
- Every check stays alive past 20 s and exits on its own.
- Each uses ~2–4 s of CPU over 38–56 s, so it is mostly waiting.
- With no timeout, the same command returns a valid signature.
Happy to test a candidate build on this machine.