Repository navigation
install: deliver a manifest task's waiters on every pass of run_tasks - #44414
Conversation
…r a manifest; yarn.lock migration with an unusable scope registry
|
Status Reproduced on main and on 1.4.2 with a loopback registry:
Merged as 80de08a. |
|
Updated 4:51 PM PT - Oct 2nd, 2026
✅ @dylan-conway, your commit 3624e2a0d7930e7332bada6f8b98771b2b12f51d passed in 🧪 To try this PR locally: bunx bun-pr 44414That installs a local version of the PR into your bun-44414 --bun |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 🧰 Additional context used📚 Code guidelines (2)No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (5)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review. WalkthroughManifest task processing and named dependency updates changed. Pending manifest tasks can show progress, missing task-queue entries are skipped, and update errors are returned. New tests cover auto-installed peers, download failures, and installation after Yarn lockfile migration. ChangesInstall and dependency update handling
Suggested reviewers: Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to This change fixes named updates that leave an auto-installed peer unresolved, and it fixes a yarn.lock migration panic. It adds regression tests for both. No concrete merge-blocking risk was found in the supplied context. The author says the final build and measurements were still pending. 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
Problem
bun update <dep> <peer>exits 1 witherror: util@^1.0.0 failed to resolvewhen<peer>is an auto-installed peer that nothing else depends on.enqueue_peer_rows(src/install/update_transitive.rs:452) callspopulate_manifest_cachewhile dependencies wait for manifests. Its wait runsrun_tasksin manifests-only mode, which skips their waiter lists (runTasks.rs:662,:1070).bun installover a yarn.lock with an unusable scope registry:panic: infallible: task queued.Fix
run_taskstakes the waiter list of a finished manifest request on every pass. A prefetch request has none.print_logreturnsInstallFailedwhen the log it resets holds an error.populate_manifest_cachestarts the progress bar before its wait (panic: downloads_node active).bun-update-transitive.test.ts, 1 inyarn-lock-migration.test.ts. All fail without the fix.--latestprefetch stays open.Background
task_queuemaps request ids to waiters. Onlyrun_tasksretires requests.populate_manifest_cachefetches manifests ahead of use (bun outdated, barebun update, five more entrances). Its requests have no waiters.Downsides
task_queuelookup per stored manifest: 15 more instructions, no insert, no allocation.Resolving dependenciesline.bun update --latest <name>exits 1 and saves nothing. It exited 0 and saved bun.lock.Notes
Repro. A loopback registry has
util@1.0.0withpeerDependencies: { core: "^1.0.0" }andcore@1.0.0. The project depends onutil ^1.0.0.bun install, thenbun update util core: exit 1,error: util@^1.0.0 failed to resolve, nothing saved. The result is the same afterutil@1.1.0andcore@1.1.0exist, and in either name order. The failure is in every release since 1.4.0 (#38333 addedenqueue_peer_rows).Why 1.4.2 passed one shape. When
corealso depends onutil, 1.4.2 moved both packages. The re-resolved peer made a newcore@1.1.0, its dependency started a tarball download of a newutil, and the extract arm ofrun_tasksran the waiter list of theutilmanifest. #43122 removed that download. No revert is needed: the waiter was already dropped, and shapes without that download fail on 1.4.2 too.Mechanism.
bun updatedoes not read the manifest disk cache, so each queued dependency starts a manifest request and parks aTaskCallbackContext::Dependencyintask_queue(PackageManagerEnqueue.rs:1331).populate_manifest_cacheflushes and schedules every queued request and waits until the manager has no pending task. In that wait the old code stored the manifest and tookcontinuebefore thetask_queuetake.network_dedupe_mapkeeps the request id, so nothing asks again. The dependency stays unresolved.Forms that failed and now pass (each is a test). Both name orders. A glob next to the peer and
*.--latest. A transitive dependency named next to the peer. The peer named from a workspace member. The peer itself added to package.json. A dependency, an optional dependency, an override or afile:folder added to package.json beforebun update <peer>.--prefer-offlinewith only the peer's manifest cached. A patched dependency named next to the peer. Two forms were silent on main. With an optional dependency added to package.json,bun update <peer>exits 0 and saves a bun.lock without it.bun update <optional-dep> <peer>exits 0, writes"util": ""to package.json and saves a bun.lock with no packages.print_log. It has two callers,enqueue_peer_rowsandplan_edges. Both print and reset the log in the middle of an install, andinstall_with_managerreadslog.has_errors()later to decide whether to save. With the waiters delivered inside the prefetch wait, an error of such a dependency reaches the first caller: a tarball 404 or an integrity failure for a dependency that the request does not name. The second caller has the same defect on main:bun update --latest <name>runsplan_edgesafter the first resolve wave, so a tarball 404 in that wave printserror: GET ... - 404, exits 0 and saves bun.lock and package.json, wherebun update <name>exits 1 and saves nothing. Two tests pin both callers. Each fails without the guard.Migration abort.
Packages::Allreturns at the first manifest request it cannot start, after it scheduled earlier ones.fetch_necessary_package_metadata_after_yarn_or_pnpm_migrationdrops that error. The install wait then completed a request with notask_queueentry and hit.expect("infallible: task queued"). Release build of the merge base: exit 134. This branch: exit 0, bun.lock written.Progress bar.
start_manifest_taskstarts the bar only when it creates a request. When every name is cached or already requested by a queued dependency, the prefetch wait started with no bar, and the first pass ofrun_tasksnames the bar if a manifest download has completed by then. Real timing did not hit the window: 0 panics in 200 pty runs on each build. With the main thread paused for 400 ms before that first pass (gdb), a release build of main panics withdownloads_node activein 5 of 5 runs, for the peer added to package.json and for--prefer-offlinewith only the peer's manifest cached. This branch: 0 of 25 runs. No test covers it, because it needs a pty and that pause. One more wait has the same window and is not changed here: under--latest, the prefetch ends the bar thatresolve_pending_tasksstarted, and the wait atinstall_with_manager.rs:2001then runs without one (3 of 3 panics with the same pause on this head, 0 of 20 with real timing).Measurements (release builds of the merge base 4b02e10 and of this branch, unless noted).
run_tasks_erased, pass of a normal install: parsed-manifest arm 35 -> 33 instructions from the manifest store to theprocess_dependency_list_for_ctxcall, 304 arm 34 -> 33. The compare-and-branch onmanifests_onlyis gone at both sites. Whole function: 6517 -> 6448 instructions.run_tasks_erased: 33,102 -> 32,764 bytes.populate_manifest_cache: 3,724 -> 3,756 bytes. Release.text: 0 bytes delta (80,679,983 both). Stripped binary: 80,848,456 bytes both. These sizes are from the first push. The laterprint_logchange adds one compare and one early return.bun outdated, 30 dependencies, empty manifest cache): 30task_queuelookups (0 on the merge base), 0 inserts, 0 waiter deliveries. After the manifest store the pass runs 20 instructions for each manifest, 12 of them inHashMap::get_index. The merge base runs 5.bun update util core1 of 1 delivered once. Withcoredepending onutil: 2 of 2.bun update '*'over 40 direct dependencies and 1 peer: 40 of 40 delivered once, 41 manifest requests with no duplicate, 41 packages moved,bun install --frozen-lockfilepasses. The take in the extract arm found 0 non-empty lists.bun update util corewith both packages newer: 1GET /util, 1GET /core, then the 2 tarballs.bun update core nope: 0 requests, same reject text.bun update utiland ofbun update core: 0 changed lines.AnyEventLoop::tick_raw, debug builds, two runs each):bun update util3 and 5 on both builds.bun update core3 and 5 to 6 on the merge base, 3 and 6 on this branch.Other suites run. The update, audit, yarn and pnpm migration, add, lock, workspaces, catalogs and
update -isuites pass.bun-install.test.tsfails the same 13 cases with and without the change (they need the public network).Left for later.
--latestprefetch (see Progress bar). The place for it is the top of eachrun_taskspass.install_peer = true. Only non-peer dependencies can be parked there today.MANIFESTS_ONLYnow only keeps the parsed-manifest arm from naming the progress bar.error: Unexpected registryand marks the run as failed). Before, bun.lock was still saved. Now nothing is saved.manifests_onlygate and a helper that ends in.expect("infallible: task queued"), so a rebase of it over this change must drop them. install: re-resolve rows whose override or catalog value changed in the package.json write-back #43981 adds anotherpopulate_manifest_cachecall inside an install, which this change makes safe in any call order.