From e35a000ee61e831bd16834c3cd4ef2d5f977d5b7 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 26 Nov 2025 11:49:15 -0500 Subject: [PATCH 1/3] tryfix --- BUG_ANALYSIS.md | 101 ++++++++++++++++++++++++++++++++++++++++++++++ src/Operations.jl | 8 ++++ 2 files changed, 109 insertions(+) create mode 100644 BUG_ANALYSIS.md diff --git a/BUG_ANALYSIS.md b/BUG_ANALYSIS.md new file mode 100644 index 0000000000..ad0a91ad51 --- /dev/null +++ b/BUG_ANALYSIS.md @@ -0,0 +1,101 @@ +# Bug Analysis: Missing Manifest Entry Error + +## Error Message +``` +`LLVM_jll=...` depends on `libLLVM_jll=...`, but no such entry exists in the manifest. +``` + +Also seen with: `Enzyme` → `ADTypes` + +## Key Observations +1. **Intermittent** - doesn't always happen +2. **Fresh depots affected** - not just stale cache +3. **Affects**: Julia 1.12.x, 1.13.0-alpha1, nightly +4. **Cleaning depot fixes it** (for cached cases) + +## The Bug Mechanism + +### How manifests are built: + +1. `deps_graph()` builds resolver graph, populates `uuid_to_name` dict +2. `Resolve.resolve()` returns `vers::Dict{UUID, VersionNumber}` +3. For each UUID in `vers`, add to `pkgs` if not present +4. For each pkg in `pkgs`, query deps from registry → `deps_map[uuid]` +5. `update_manifest!` sets `entry.deps = deps_map[uuid]` +6. `prune_manifest()` keeps only reachable packages + +### The Problem + +**Step 4 queries ALL deps (including weak deps) from registry data:** +```julia +deps_for_version = Registry.query_deps_for_version( + deps_map_compressed, weak_deps_map_compressed, # ← BOTH! + pkg.uuid, pkg.version +) +``` + +This can include UUIDs that are NOT in `pkgs` because: +- They're weak deps that weren't resolved (solver chose "uninstalled") +- They're stdlibs handled specially + +### Why Ordering Matters (Your Insight) + +The iteration over `vers` (a Dict) at line 842-851 is **non-deterministic**: + +```julia +for (uuid, ver) in vers # Dict iteration order varies! + ... + push!(pkgs, PackageSpec(...)) +end +``` + +Then at lines 854-880, for each `pkg in pkgs`: +```julia +for uuid in deps_for_version + d[names[uuid]] = uuid # Requires uuid to be in names! +end +``` + +If the `names` dict wasn't fully populated due to ordering, or if a dependency UUID exists in `deps_for_version` but the package itself wasn't added to `pkgs` (and thus won't be in manifest), we get a corrupt manifest. + +## Root Cause Hypothesis + +**The `is_stdlib` check at line 849 uses current Julia's stdlibs, not `julia_version`:** + +```julia +name = is_stdlib(uuid) ? stdlib_infos()[uuid].name : registered_name(registries, uuid) +``` + +But `deps_graph` uses `stdlibs_for_julia_version` which could differ! + +This inconsistency, combined with non-deterministic Dict iteration, could cause: +1. A stdlib to be in the resolver graph (treated as stdlib for `julia_version`) +2. But NOT recognized as stdlib when building `pkgs` (using `VERSION`) +3. Leading to it being in a package's `deps` but not in the manifest + +## Relevant Commits + +- `cdc17a0d7` "allow having unknown weak dependencies" - filters unavailable weak deps during resolution, but doesn't help with manifest reading +- `efe1eaf7a` "stop uncompressing registry data" - only on master + +## Potential Fix + +Line 849 should use `is_stdlib(uuid, julia_version)` instead of `is_stdlib(uuid)`: + +```julia +# Current (buggy): +name = is_stdlib(uuid) ? stdlib_infos()[uuid].name : registered_name(registries, uuid) + +# Fixed: +name = is_stdlib(uuid, julia_version) ? + get_last_stdlibs(julia_version)[uuid].name : + registered_name(registries, uuid) +``` + +Additionally, `deps_map` should only include deps that are actually in `pkgs`/`vers`, not all deps from registry. + +## To Verify + +1. Check if `julia_version != VERSION` in failing cases +2. Add logging to see if stdlib detection differs between resolver and pkgs-building +3. Check if the missing package (libLLVM_jll, ADTypes) is a stdlib for one version but not another diff --git a/src/Operations.jl b/src/Operations.jl index 3cf9c77fe5..54dffa2d7d 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -850,6 +850,10 @@ function resolve_versions!( push!(pkgs, PackageSpec(; name = name, uuid = uuid, version = ver)) end end + + # Collect all UUIDs that will be in the manifest + pkgs_uuids = Set{UUID}(pkg.uuid for pkg in pkgs) + final_deps_map = Dict{UUID, Dict{String, UUID}}() for pkg in pkgs load_tree_hash!(registries, pkg, julia_version) @@ -857,6 +861,8 @@ function resolve_versions!( if pkg.uuid in keys(fixed) deps_fixed = Dict{String, UUID}() for dep in keys(fixed[pkg.uuid].requires) + # Only include deps that are actually in the manifest + dep in pkgs_uuids || continue deps_fixed[names[dep]] = dep end deps_fixed @@ -871,6 +877,8 @@ function resolve_versions!( pkg.uuid, pkg.version ) for uuid in deps_for_version + # Only include deps that are actually in the manifest + uuid in pkgs_uuids || continue d[names[uuid]] = uuid end d From b95df49270022361f9ab14be28125adbece93829 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Wed, 26 Nov 2025 12:05:47 -0500 Subject: [PATCH 2/3] fix understanding doc --- BUG_ANALYSIS.md | 94 +++++++++++++------------------------------------ 1 file changed, 25 insertions(+), 69 deletions(-) diff --git a/BUG_ANALYSIS.md b/BUG_ANALYSIS.md index ad0a91ad51..43cdac38f2 100644 --- a/BUG_ANALYSIS.md +++ b/BUG_ANALYSIS.md @@ -1,6 +1,7 @@ # Bug Analysis: Missing Manifest Entry Error ## Error Message + ``` `LLVM_jll=...` depends on `libLLVM_jll=...`, but no such entry exists in the manifest. ``` @@ -8,94 +9,49 @@ Also seen with: `Enzyme` → `ADTypes` ## Key Observations -1. **Intermittent** - doesn't always happen -2. **Fresh depots affected** - not just stale cache -3. **Affects**: Julia 1.12.x, 1.13.0-alpha1, nightly -4. **Cleaning depot fixes it** (for cached cases) - -## The Bug Mechanism -### How manifests are built: +- **Intermittent** - doesn't always happen +- **Fresh depots affected** - not just stale cache +- **Affects**: Julia 1.12.x, 1.13.0-alpha1, nightly +- **Cleaning depot fixes it** (for cached cases) -1. `deps_graph()` builds resolver graph, populates `uuid_to_name` dict -2. `Resolve.resolve()` returns `vers::Dict{UUID, VersionNumber}` -3. For each UUID in `vers`, add to `pkgs` if not present -4. For each pkg in `pkgs`, query deps from registry → `deps_map[uuid]` -5. `update_manifest!` sets `entry.deps = deps_map[uuid]` -6. `prune_manifest()` keeps only reachable packages +## Root Cause -### The Problem +When building `deps_map` for manifest entries, `query_deps_for_version` returns ALL deps including weak deps: -**Step 4 queries ALL deps (including weak deps) from registry data:** ```julia deps_for_version = Registry.query_deps_for_version( - deps_map_compressed, weak_deps_map_compressed, # ← BOTH! + deps_map_compressed, weak_deps_map_compressed, # ← includes weak deps! pkg.uuid, pkg.version ) ``` -This can include UUIDs that are NOT in `pkgs` because: -- They're weak deps that weren't resolved (solver chose "uninstalled") -- They're stdlibs handled specially - -### Why Ordering Matters (Your Insight) - -The iteration over `vers` (a Dict) at line 842-851 is **non-deterministic**: - -```julia -for (uuid, ver) in vers # Dict iteration order varies! - ... - push!(pkgs, PackageSpec(...)) -end -``` - -Then at lines 854-880, for each `pkg in pkgs`: -```julia -for uuid in deps_for_version - d[names[uuid]] = uuid # Requires uuid to be in names! -end -``` - -If the `names` dict wasn't fully populated due to ordering, or if a dependency UUID exists in `deps_for_version` but the package itself wasn't added to `pkgs` (and thus won't be in manifest), we get a corrupt manifest. +But weak deps that weren't triggered (no extension trigger present) are NOT in `vers`/`pkgs` because the resolver marked them as "uninstalled". -## Root Cause Hypothesis +Result: `entry.deps` contains UUIDs that have no corresponding manifest entry. -**The `is_stdlib` check at line 849 uses current Julia's stdlibs, not `julia_version`:** - -```julia -name = is_stdlib(uuid) ? stdlib_infos()[uuid].name : registered_name(registries, uuid) -``` +## Why Existing Fix Doesn't Help -But `deps_graph` uses `stdlibs_for_julia_version` which could differ! +Commit `cdc17a0d7` ("allow having unknown weak dependencies") handles weak deps that are **not in any registry**. It filters them from `fixed` before resolution. -This inconsistency, combined with non-deterministic Dict iteration, could cause: -1. A stdlib to be in the resolver graph (treated as stdlib for `julia_version`) -2. But NOT recognized as stdlib when building `pkgs` (using `VERSION`) -3. Leading to it being in a package's `deps` but not in the manifest +This bug is about weak deps that ARE registered but weren't resolved because the trigger wasn't needed. -## Relevant Commits +## The Fix -- `cdc17a0d7` "allow having unknown weak dependencies" - filters unavailable weak deps during resolution, but doesn't help with manifest reading -- `efe1eaf7a` "stop uncompressing registry data" - only on master - -## Potential Fix - -Line 849 should use `is_stdlib(uuid, julia_version)` instead of `is_stdlib(uuid)`: +Filter `deps_map` to only include UUIDs that are actually in `pkgs`: ```julia -# Current (buggy): -name = is_stdlib(uuid) ? stdlib_infos()[uuid].name : registered_name(registries, uuid) +pkgs_uuids = Set{UUID}(pkg.uuid for pkg in pkgs) -# Fixed: -name = is_stdlib(uuid, julia_version) ? - get_last_stdlibs(julia_version)[uuid].name : - registered_name(registries, uuid) +for uuid in deps_for_version + uuid in pkgs_uuids || continue # ← Skip deps not in manifest + d[names[uuid]] = uuid +end ``` -Additionally, `deps_map` should only include deps that are actually in `pkgs`/`vers`, not all deps from registry. - -## To Verify +## Why Intermittent? -1. Check if `julia_version != VERSION` in failing cases -2. Add logging to see if stdlib detection differs between resolver and pkgs-building -3. Check if the missing package (libLLVM_jll, ADTypes) is a stdlib for one version but not another +Unclear. Possibly related to: +- Registry caching/state +- Which package versions get selected +- Timing of when weak deps get evaluated From f18b1e4bf6eed7de87ba0b5bb7b1f53a6c562090 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 9 Dec 2025 10:38:24 -0500 Subject: [PATCH 3/3] Delete BUG_ANALYSIS.md --- BUG_ANALYSIS.md | 57 ------------------------------------------------- 1 file changed, 57 deletions(-) delete mode 100644 BUG_ANALYSIS.md diff --git a/BUG_ANALYSIS.md b/BUG_ANALYSIS.md deleted file mode 100644 index 43cdac38f2..0000000000 --- a/BUG_ANALYSIS.md +++ /dev/null @@ -1,57 +0,0 @@ -# Bug Analysis: Missing Manifest Entry Error - -## Error Message - -``` -`LLVM_jll=...` depends on `libLLVM_jll=...`, but no such entry exists in the manifest. -``` - -Also seen with: `Enzyme` → `ADTypes` - -## Key Observations - -- **Intermittent** - doesn't always happen -- **Fresh depots affected** - not just stale cache -- **Affects**: Julia 1.12.x, 1.13.0-alpha1, nightly -- **Cleaning depot fixes it** (for cached cases) - -## Root Cause - -When building `deps_map` for manifest entries, `query_deps_for_version` returns ALL deps including weak deps: - -```julia -deps_for_version = Registry.query_deps_for_version( - deps_map_compressed, weak_deps_map_compressed, # ← includes weak deps! - pkg.uuid, pkg.version -) -``` - -But weak deps that weren't triggered (no extension trigger present) are NOT in `vers`/`pkgs` because the resolver marked them as "uninstalled". - -Result: `entry.deps` contains UUIDs that have no corresponding manifest entry. - -## Why Existing Fix Doesn't Help - -Commit `cdc17a0d7` ("allow having unknown weak dependencies") handles weak deps that are **not in any registry**. It filters them from `fixed` before resolution. - -This bug is about weak deps that ARE registered but weren't resolved because the trigger wasn't needed. - -## The Fix - -Filter `deps_map` to only include UUIDs that are actually in `pkgs`: - -```julia -pkgs_uuids = Set{UUID}(pkg.uuid for pkg in pkgs) - -for uuid in deps_for_version - uuid in pkgs_uuids || continue # ← Skip deps not in manifest - d[names[uuid]] = uuid -end -``` - -## Why Intermittent? - -Unclear. Possibly related to: -- Registry caching/state -- Which package versions get selected -- Timing of when weak deps get evaluated