Skip to content

fix(onboarding): show real install errors and fix concurrent install state - #2658

Merged
wpfleger96 merged 3 commits into
mainfrom
duncan/install-error-ux
Jul 24, 2026
Merged

fix(onboarding): show real install errors and fix concurrent install state#2658
wpfleger96 merged 3 commits into
mainfrom
duncan/install-error-ux

Conversation

@wpfleger96

Copy link
Copy Markdown
Member

Fixes two bugs in the ACP runtime setup screen (onboarding) and Doctor settings panel that caused users to see unhelpful generic errors on install failure and lose the first install's state when starting two installs back-to-back.

What this fixes

Bug A — real install errors discarded in onboarding. RuntimeCard received the detailed install error string from getInstallErrorMessage (step name, stderr, exit code, actionable hint) but rendered a hardcoded "Installation couldn't be completed. Try again." tooltip regardless of the actual failure. The rich string was computed and stored in installResults state, then ignored at render. Fix: pass installError as detail to RuntimeErrorTooltip. Add whitespace-pre-line to the tooltip content span so multi-line hint + step-detail strings render correctly.

Users on Windows with no Node.js will now see the actual Git Bash error and the actionable hint instead of a generic message. Users with npm permission issues on macOS see the EACCES detail and the npm config set prefix remediation hint.

Bug B — concurrent installs lose the first card's state. RuntimeProvidersSection (onboarding) and DoctorSettingsPanel each shared one useInstallAcpRuntimeMutation() instance across all runtime cards/rows. react-query v5 per-mutate() callbacks (onSuccess/onError) only fire for the latest mutate() call on a shared instance — starting install B silently drops install A's result callback, so card A never shows success or failure. Additionally, isPending and mutation.variables tracked only the latest call, making card A's spinner vanish mid-install when card B started.

Fix: move useInstallAcpRuntimeMutation() into each RuntimeCard (onboarding) and RuntimeRow (Doctor panel) so every card owns its own mutation instance with independent isPending and callbacks. The shared installMutation + handleInstall in RuntimeProvidersSection and the lifted installResults/installingIds/handleInstall in DoctorSettingsPanel are removed.

Non-happy-path scenarios

  • Install A fails while B is in-flight: each card has its own mutation instance; A's onError fires independently, A's card shows the error, B's spinner continues unaffected.
  • Both fail: each card's onError fires separately, both show their own error detail.
  • A succeeds while B fails: A's card shows success (runtime query invalidation triggers re-check → READY), B's card shows its error detail.

The RETRY INSTALL label flow (installError → installLabel in RuntimeStatus) is unchanged — retrying a card's install clears its prior result and re-fires its own mutation.

Files changed

  • desktop/src/features/onboarding/ui/SetupStep.tsxRuntimeCard owns its mutation; RuntimeProvidersSection shared mutation removed
  • desktop/src/features/onboarding/ui/RuntimeErrorTooltip.tsxwhitespace-pre-line on tooltip content
  • desktop/src/features/settings/ui/DoctorSettingsPanel.tsxRuntimeRow owns its mutation + result state; section-level lifted state removed
  • desktop/src/shared/lib/installError.test.mjs — two new test cases: double-newline separator format and multi-step last-step-only behavior

…state

Two bugs in the onboarding runtime setup screen and the Doctor settings
panel:

Bug A: RuntimeCard received the detailed install error string (step,
stderr, exit code, actionable hint) via installResults state but rendered
a hardcoded 'Installation couldn't be completed. Try again.' tooltip
instead. The rich string from getInstallErrorMessage was computed and
stored, then thrown away at render. Fix: pass installError as detail to
RuntimeErrorTooltip. Add whitespace-pre-line to the tooltip content span
so hint + step blocks render on separate lines.

Bug B: RuntimeProvidersSection (onboarding) and DoctorSettingsPanel
shared one useInstallAcpRuntimeMutation instance across all runtime
cards/rows. react-query v5 per-mutate callbacks (onSuccess/onError) only
fire for the latest mutate() call on a shared instance — starting install
B silently drops install A's result callback. Additionally, isPending and
mutation.variables tracked only the latest call, so card A's spinner
vanished as soon as card B's install started. Fix: move
useInstallAcpRuntimeMutation into each RuntimeCard / RuntimeRow so every
card owns its own mutation instance with independent isPending and
callbacks. RuntimeProvidersSection's shared installMutation and
handleInstall are removed; DoctorSettingsPanel's lifted installResults /
installingIds / handleInstall are replaced by per-row state.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 requested a review from a team as a code owner July 24, 2026 01:10
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 2 commits July 23, 2026 21:42
F1 (Doctor stale error): add resetEpoch prop to RuntimeRow; DoctorSettingsPanel
bumps epoch on Check again; RuntimeRow useEffect([resetEpoch]) clears local
installResult so stale failures are gone after a catalog refetch.

F2 (no concurrent regression test): extend e2eBridge with installAcpRuntimeByRuntime
config supporting per-runtime delay/result/call-sequences with independent
counters. Add Doctor concurrent + stale-clear E2E test (08) and onboarding
concurrent E2E test (both-fail-one-succeeds with rich multiline tooltip).

F3 (tooltip visually clipped): RuntimeErrorTooltip content now has
max-h-48 overflow-y-auto break-words in addition to whitespace-pre-line,
bounding tall/wide stderr output.

Doctor test assertion: codex success banner (installSuccess && availability
!= 'available') races with the catalog refresh that immediately returns
availability 'available'. Assert codexToggle.toBeChecked() instead — stable
whether isOn is driven by installSuccess or isAvailable.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
… CSS

The previous tooltip E2E test fed a short 5-line payload and used
toContainText() — passes with or without max-h-48/break-words.

Replace fixture with a 19-line realistic Windows EACCES error including
a long unbroken path (C:\Users\...\claude-agent-acp.exe) that exercises
both overflow axes. Add overflow dimension assertions on the detail span:

  • scrollHeight > clientHeight: proves max-h-48 + overflow-y-auto are active
    (without them, tall content has no scroll container; assertion fails)
  • scrollTop can advance: proves the span actually scrolls
  • scrollWidth <= clientWidth: proves break-words prevents horizontal overflow
    (verified red-on-old: removing break-words causes this assertion to fail)

Also add overflow-x-hidden to RuntimeErrorTooltip's detail span so that
overflow-y: auto and overflow-x: hidden together make the scroll container
unambiguous on both axes. Locator uses page.locator().first() because
Radix tooltip portals place a visible copy and a hidden duplicate in the DOM.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 merged commit 9731cd8 into main Jul 24, 2026
25 checks passed
@wpfleger96
wpfleger96 deleted the duncan/install-error-ux branch July 24, 2026 16:12
@wpfleger96

Copy link
Copy Markdown
Member Author

UI states — PR #2658

Onboarding: rich install-error tooltip

Bounded, scrollable dark tooltip showing the real npm EACCES stderr; long Windows path wraps instead of overflowing horizontally.
onboarding-error-tooltip

Onboarding: concurrent installs — independent card states

Both Claude Code and Codex installs in flight simultaneously; each card tracks its own state independently (no last-caller-wins drop).
onboarding-concurrent-installs

Doctor: row error — real step failure detail

Doctor panel Codex row after a failed install, showing the actual step stderr ("npm ERR! code E404 / npm ERR! 404 Not Found") instead of the generic message.
doctor-row-error

Doctor: Check again — stale error cleared

Same Claude Code row after clicking "Check again": the stale install error is cleared via resetEpoch, row returns to healthy state.
doctor-check-again-cleared

wpfleger96 pushed a commit that referenced this pull request Jul 24, 2026
wpfleger96 added a commit that referenced this pull request Jul 24, 2026
## Buzz Desktop release v0.4.25

### Changes since v0.4.24:

- fix(discovery): spawn PowerShell install commands natively on Windows
([#2750](#2750))
([`f3981dbfe`](f3981db))
- fix(desktop): use augmented PATH for model discovery subprocess
([#2753](#2753))
([`3bd3a014c`](3bd3a01))
- Improve huddle audio failure handling
([#2578](#2578))
([`fb4a801ad`](fb4a801))
- fix(onboarding): show real install errors and fix concurrent install
state ([#2658](#2658))
([`9731cd818`](9731cd8))
- feat(node): add Windows managed Node.js fallback (win-x64 + win-arm64)
([#2661](#2661))
([`596386ee5`](596386e))
- fix(desktop): parse runtime team instructions section
([#2645](#2645))
([`269ef357f`](269ef35))
- Match create-channel template selector styling
([#2654](#2654))
([`72bbaece4`](72bbaec))
- feat(desktop): make pull request reviews actionable
([#2510](#2510))
([`9081ab0ec`](9081ab0))
- fix(desktop): shared-compute usability — share toggle, usage
indicator, model resync
([#2448](#2448))
([`9cc9652c7`](9cc9652))
- fix(desktop): refine focused thread dismissal targets
([#2644](#2644))
([`c86c4f59c`](c86c4f5))
- Clarify agent harness defaults in create flow
([#2601](#2601))
([`76aeae703`](76aeae7))
- fix: expose community icon control on open relays
([#2640](#2640))
([`e341b09cb`](e341b09))

**To release:** merge this PR. The tag and build will happen
automatically.
wpfleger96 added a commit that referenced this pull request Jul 24, 2026
…stallSuccess plumbing

Rebase conflict resolution in the prior commit incorrectly preserved main's
#2658 per-card mutation architecture while dropping three Atish-authored hunks:
- guidance div (doctor-runtime-guidance-*) missing from RuntimeRow
- not_installed availability not included in CLI-needed chip branch
- installSuccess binding restored instead of deleted; installSuccess banner kept

This commit replaces the panel with Atish's final state from ca34471:
- guidance div reinserted between RuntimeHeader and config_invalid paragraph
- not_installed added to CLI-needed chip (alongside cli_missing)
- installSuccess variable, prop threading through Row/Header/Actions, isOn
  computed var, disabled expression, and green banner all removed
- data-testid on install-error paragraph preserved (added by fc15d79)
- per-card resetEpoch architecture from #2658 kept (Atish's final intent)

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant