[Bug]: Unconditional PowerShell profile discovery delays Windows desktop startup
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/desktop
Steps to reproduce
- On Windows, ensure
pwsh.exeis installed. The affected machine also has Windows PowerShell 5.1 available aspowershell.exe. - Ensure
node.exeis already available from the inherited/no-profilePATH:pwsh.exe -NoProfile -NonInteractive -Command "(Get-Command node).Source" - Use a normal PowerShell profile that performs environment initialization. No artificial delay or
Start-Sleepis required for this reproduction. - Fully exit T3 Code and verify no prior T3 Code/Electron process remains, to keep this separate from #4395.
- Start the packaged desktop app normally.
- Inspect:
%USERPROFILE%\.t3\userdata\logs\desktop.trace.ndjson* - Find the
desktop.shellEnvironment.installWindowsEnvironmentandreadWindowsEnvironmentspans. - Repeat several cold launches.
On the affected machine, every observed startup performs both:
- a
-NoProfileenvironment read; and - a profile-loaded environment read.
This happens even though node.exe is already available after the first read.
Expected behavior
Windows environment hydration should first construct a baseline environment without loading user profiles.
If that baseline is sufficient to launch the required runtime, profile discovery should be skipped. This was the behavior introduced in #1729: load the PowerShell profile only when node is not available from the baseline environment.
If profile discovery is still required—for example, to obtain FNM variables or additional profile-defined paths—the complete discovery operation should have one total startup budget. Trying another PowerShell candidate should consume the remaining budget rather than start a fresh timeout.
A profile failure or timeout should remain best-effort: preserve the baseline environment and continue startup.
Actual behavior
The affected implementation unconditionally performs the no-profile read and then the profile-loaded read:
Environment hydration also occurs before user-data resolution, lifecycle registration, the single-instance configuration, and app.whenReady().
Each read tries the following candidates sequentially:
pwsh.exe
powershell.exe
Each candidate receives a new five-second timeout. Because both no-profile and profile-loaded reads use this loop, failures can consume up to four separate timeout budgets, plus process-termination overhead.
Measured packaged-app startup traces
Twenty-three successful Windows desktop starts were measured between 2026-07-15 and 2026-07-23:
| Startup phase | Count | Minimum | Median | p95 | Maximum | Mean |
|---|---|---|---|---|---|---|
| No-profile read | 23 | 1.16 s | 2.06 s | 3.31 s | 3.32 s | 2.11 s |
| Profile-loaded read | 23 | 2.21 s | 4.20 s | 9.70 s | 11.35 s | 4.34 s |
| Total shell hydration | 23 | 3.54 s | 6.31 s | 12.21 s | 13.35 s | 6.45 s |
Every trace contained exactly two aggregate environment reads in this order: no-profile, then profile-loaded.
Two of the 23 starts reached profile-probe timeouts:
trace 8be0a1df…
pwsh.exe profile probe: timed out
powershell.exe profile probe: timed out
profile phase: 11,354.5 ms
total shell hydration: 13,348.0 ms
trace c03677f6…
pwsh.exe profile probe: timed out
powershell.exe fallback: succeeded
profile phase: 9,700.2 ms
total shell hydration: 12,206.7 ms
Across these traces, shell hydration accounted for a median 98.5% of the main-process desktop.startup span.
These measurements cover only main-process startup through the bootstrap request. They do not measure the additional time until the renderer, backend, or environment reconnect is usable; those layers can make the user-visible wait longer.
Direct profile/no-profile comparison
Five alternating executions of the same pwsh.exe environment-capture shape used by the app produced:
-NoProfile:
minimum: 671.6 ms
median: 1,014.7 ms
maximum: 1,738.5 ms
Profile loaded:
minimum: 3,516.0 ms
median: 3,881.5 ms
maximum: 4,736.5 ms
For this environment:
PATH identical: yes
node available in both: yes
FNM_DIR present: no
FNM_MULTISHELL_PATH present: no
The profile-loaded probe therefore added several seconds without changing any of the values collected by the app.
Suspected regression
PR #1729 intentionally implemented a conditional fallback:
- read
PATHwithout loading profiles; - merge inherited and known CLI paths;
- return immediately if
nodeis available; - load the profile only if
noderemains unavailable.
Its tests asserted that the environment reader was invoked only once when node was already available.
The unconditional profile read appears to have been introduced during the large Effect port in #2546. That PR does not document an intended change to the Windows profile-loading policy, so this looks like a behavioral regression, although maintainer confirmation would be useful.
One open design question is whether the unconditional read was intended to collect FNM_DIR, FNM_MULTISHELL_PATH, or the complete profile PATH even when node already exists. If so, that requirement should be explicit and the work should still be bounded or removed from the synchronous startup path.
Possible acceptance criteria
- When the no-profile baseline satisfies the documented runtime requirements, no profile-loaded PowerShell process is started.
- When a profile is required, all PowerShell candidates share one total deadline.
- A timeout preserves and installs the usable baseline environment.
- Tests cover:
- runtime already available without a profile;
- runtime only available after profile loading;
- a hanging
pwsh.execandidate; - fallback to
powershell.exewithin the remaining budget; - preservation of required FNM variables.
- Traces record whether profile discovery was required, which candidate ran, its outcome, and the remaining or total budget.
Related work checked
- #2951 removed the
cmd.exewrapper but did not change profile-loading policy. - #3383 added structured failures and a five-second per-command timeout, not a total discovery budget.
- #4291 and #4221 bound server-side editor discovery; they do not affect desktop shell hydration.
- #4228 improves Windows process diagnostics but does not affect this startup path.
- #4395 tracks rejected duplicate launches leaving Electron processes and is intentionally separate from this issue.
- #3611 concerns a WSL/runtime-version startup loop rather than desktop PowerShell discovery.
I did not find an open issue or PR specifically tracking unconditional Windows profile discovery or its additive candidate timeouts.
Impact
Major degradation or frequent failure
The delay occurs on every observed launch of the affected installation. It postpones all later desktop initialization and can add more than ten seconds before renderer/backend startup delays are considered.
Version or commit
T3 Code 0.0.29-nightly.20260722.878
Commit 9a0a07167f0623c3a7db0ffeff2e3939760309df
Electron 41.5.0
Environment
Windows 11 x64, build 26200.8875
PowerShell 7.6.4
Windows PowerShell 5.1
Node.js 22.14.0, already available from the no-profile PATH
Timezone: America/Bogota (UTC-05)
Logs or stack traces
Observed startup count: 23
Observation period: 2026-07-15 through 2026-07-23
No-profile read median: 2,064.0 ms
Profile-loaded read median: 4,201.4 ms
Total hydration median: 6,305.3 ms
Total hydration p95: 12,206.7 ms
Total hydration maximum: 13,348.0 ms
Profile timeouts observed: 2
Starts executing both profile modes: 23 of 23
Secrets, profile contents, and absolute user paths have been omitted.
Screenshots, recordings, or supporting files
No screenshot is needed for the startup-only behavior. A redacted trace extract can be attached if maintainers need the underlying span events.
Workaround
Temporarily simplifying expensive PowerShell profile initialization reduces the delay, but this changes the user’s shell configuration and is not a reliable application-level workaround.
Placing node.exe on the inherited or no-profile PATH does not prevent the current implementation from loading the profile.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with apps/desktop/src/shell/DesktopShellEnvironment.ts lines 297–353 and apps/desktop/src/app/DesktopApp.ts lines 229–243, then inspect the existing environment-reader tests referenced by the issue. Verify the baseline-first path, shared deadline for profile candidates, baseline preservation on timeout, and trace coverage for whether profile discovery was required.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- electron, powershell, typescript
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100