fix(create-electron-app): surface dependency install failures during init - #4348
Draft
erickzhao wants to merge 1 commit into
Draft
fix(create-electron-app): surface dependency install failures during init#4348erickzhao wants to merge 1 commit into
erickzhao wants to merge 1 commit into
Conversation
…init
The three dependency-install tasks in `init` were marked `exitOnError: false`.
listr2 treats that as "mark this task FAILED but do not rethrow", so a failed
install never propagated: `init` resolved, the CLI exited 0, and the parent
tasks still rendered a green checkmark over the failed child.
The result is a scaffolded app that is quietly broken. Passing an unresolvable
version, for example, leaves `electron` absent from both `devDependencies` and
`node_modules` while still exiting 0:
✖ Installing common dependencies [FAILED: ...No candidates found]
✔ Finalizing dependencies
✔ Installing template dependencies
exit 0
With an unreachable registry it is worse — no dependencies and no
`node_modules` at all, still exit 0. Via the programmatic API the failure is
entirely invisible, since `interactive` defaults to false and the renderer is
silent, so `init()` resolves with no output whatsoever.
The error was not recoverable after the fact either. listr2's `report()` pushes
onto the *nested* list's `errors` array, so the top-level `runner.errors` stays
empty and a post-run check would not have caught it.
Removing the option restores the default (`exitOnError: true`), matching the
`initLink` sibling task, which already sets it explicitly. Failures now travel
the same path as every other init error and exit non-zero.
All three sites are fixed rather than only the `initNPM` one: the two template
dependency tasks are the same defect and are reachable today, since the webpack
and vite templates have non-empty dependency lists.
Note that #3219 fixed this exact class of bug for `initLink` — it moved the
option from the list level onto individual tasks and set `exitOnError: true`
for linking, but left the installs silent. The option originally arrived in the
async-ora to listr2 port (#3022); the pre-port code propagated these errors.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The three dependency-install tasks in
initwere markedexitOnError: false. listr2 treats that as "mark this task FAILED but do not rethrow", so a failed install never propagated —initresolved, the CLI exited 0, and the parent tasks still rendered a green checkmark over the failed child.The result is a scaffolded app that is quietly broken. Passing an unresolvable version (which clears
semver.cleanand then fails at install time) leaveselectronabsent from bothdevDependenciesandnode_modules, while still exiting 0:With an unreachable registry it's worse — zero dependencies, no
node_modulesat all, still exit 0.Via the programmatic API the failure is completely invisible:
interactivedefaults tofalse, sosilentRendererConditionis true, there is no✖line at all, andinit()simply resolves.The relevant listr2 code (
Task.run):The error wasn't recoverable after the fact either:
report()pushes ontothis.task.listr.errors— the innermost nested list — so top-levelrunner.errors.length === 0and a post-run error check would not have caught it.Fix
Removed the option, restoring the default (
exitOnError: true). This matches theinitLinksibling in the same list, which already setsexitOnError: trueexplicitly. Failures now travel the same path as every other init error (e.g. an unknown template) and exit non-zero.I fixed all three sites rather than only the
initNPMone. The two template-dependency tasks are the same defect and are reachable today, since the webpack and vite templates have non-empty dependency lists. Happy to split them out if you'd prefer a narrower diff.Background
This isn't a deliberate design choice.
exitOnError: falsearrived as a list-level option in the async-ora → listr2 port (#3022); the pre-port code propagated these errors. #3219 — titled "fix(core): silent failures when linking forge dependencies" — then pushed the option down onto individual tasks and setexitOnError: trueforinitLinkonly, so this exact bug class was recognized and fixed for linking while the installs were left silent.Testing
Added
packages/external/create-electron-app/spec/fast/init.spec.ts. Verified it genuinely catches the regression: with the fix reverted, 2 of the 3 tests fail withpromise resolved "undefined" instead of rejecting.yarn test:fast— 56 files, 412 passedyarn lint:js— cleancreate-electron-app <dir> --electron-version 999.999.999now exits 1 instead of 0Note for a follow-up
Init failures surface through
terminate.ts'sunhandledRejectionhandler, so the output reads "An unhandled rejection has occurred inside Forge". That wording is misleading, but it's pre-existing and shared by every init failure path (commander's.action()promise is never awaited) — worth a separate cosmetic fix rather than widening this one.🤖 Generated with Claude Code