From 3eefbbe8839315b53171523bf43fa65bde41803c Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 22 Feb 2026 19:35:40 +0900 Subject: fix(resolver): handle virtual packages and deduplicate pool exploration Virtual/meta packages (e.g. "psr/http-client-implementation") don't exist on Packagist and caused a fatal error during transitive dependency exploration. These packages are resolved via provides/replaces from other packages already in the pool, so 404 errors are now skipped. Also fix PoolBuilder::next_pending() repeatedly returning the same virtual package name by tracking explored names in a HashSet, since virtual packages are never added to inputs and the old check (inputs.any(name)) never matched them. Co-Authored-By: Claude Opus 4.6 --- crates/mozart-registry/src/resolver.rs | 20 ++++++++++++-------- crates/mozart-sat-resolver/src/pool_builder.rs | 14 +++++++++++--- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/crates/mozart-registry/src/resolver.rs b/crates/mozart-registry/src/resolver.rs index 66864f7..3e0936f 100644 --- a/crates/mozart-registry/src/resolver.rs +++ b/crates/mozart-registry/src/resolver.rs @@ -467,14 +467,18 @@ pub async fn resolve(request: &ResolveRequest) -> Result, R continue; } - let versions = handle - .block_on(packagist::fetch_package_versions( - &name, - repo_cache.as_ref(), - )) - .map_err(|e| { - ResolveError::DependencyFetchError(format!("Failed to fetch {}: {}", name, e)) - })?; + let versions = match handle.block_on(packagist::fetch_package_versions( + &name, + repo_cache.as_ref(), + )) { + Ok(v) => v, + Err(_) => { + // Virtual/meta packages (e.g. "psr/http-client-implementation") + // don't exist on Packagist. They are resolved via provides/replaces + // from other packages already in the pool. + continue; + } + }; for pv in &versions { let inputs = diff --git a/crates/mozart-sat-resolver/src/pool_builder.rs b/crates/mozart-sat-resolver/src/pool_builder.rs index 40977ef..a642fc3 100644 --- a/crates/mozart-sat-resolver/src/pool_builder.rs +++ b/crates/mozart-sat-resolver/src/pool_builder.rs @@ -13,6 +13,8 @@ pub struct PoolBuilder { added: HashSet, /// Queue of package names that need to be explored. pending_names: VecDeque, + /// Package names that have already been explored (returned by next_pending). + explored_names: HashSet, /// Platform packages to ignore. ignore_platform_reqs: HashSet, } @@ -23,6 +25,7 @@ impl PoolBuilder { inputs: Vec::new(), added: HashSet::new(), pending_names: VecDeque::new(), + explored_names: HashSet::new(), ignore_platform_reqs: HashSet::new(), } } @@ -56,10 +59,15 @@ impl PoolBuilder { /// and add them via `add_package`. pub fn next_pending(&mut self) -> Option { while let Some(name) = self.pending_names.pop_front() { - // Check if we already have any versions for this name - if !self.inputs.iter().any(|p| p.name == name) { - return Some(name); + // Skip if already explored or already has versions in inputs + if self.explored_names.contains(&name) { + continue; } + if self.inputs.iter().any(|p| p.name == name) { + continue; + } + self.explored_names.insert(name.clone()); + return Some(name); } None } -- cgit v1.3.1