From b6f58f5a9bc41bb21843a007dc2eb02205725199 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 4 Dec 2025 11:38:23 -0500 Subject: [PATCH] Fix VersionSpec to VersionNumber conversion error when freeing packages Fixes #4554 When freeing a dev'd or pinned package, `pkg.version` could remain as a `VersionSpec` in certain code paths, but `PackageEntry` requires `version::Union{VersionNumber, Nothing}`. This caused a MethodError when trying to convert VersionSpec to VersionNumber. The issue occurred because: 1. When freeing a package from dev/path/repo state, the version might not be updated through the normal resolve process 2. In some cases (e.g., pinned stdlibs), `update_package_free!` returns early without setting the version 3. The `PackageEntry` constructor was called with `pkg.version` which could be a `VersionSpec`, causing a type conversion error This fix adds defensive type checking before creating `PackageEntry` objects, converting non-VersionNumber values to `nothing`. The correct version will be set later if needed (e.g., for stdlibs). Changes: - src/Operations.jl: Added type check in update_manifest! - src/Apps/Apps.jl: Added type checks in two PackageEntry creations --- src/Apps/Apps.jl | 7 ++++--- src/Operations.jl | 7 ++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Apps/Apps.jl b/src/Apps/Apps.jl index 2cf5b28fd1..0eba27043d 100644 --- a/src/Apps/Apps.jl +++ b/src/Apps/Apps.jl @@ -219,7 +219,8 @@ function add(pkg::PackageSpec) sourcepath = source_path(ctx.env.manifest_file, pkg) project = get_project(sourcepath) # TODO: Wrong if package itself has a sourcepath? - entry = PackageEntry(; apps = project.apps, name = pkg.name, version = project.version, tree_hash = pkg.tree_hash, path = pkg.path, repo = pkg.repo, uuid = pkg.uuid) + # PackageEntry requires version::Union{VersionNumber, Nothing}, but project.version can be VersionSpec + entry = PackageEntry(; apps = project.apps, name = pkg.name, version = project.version isa VersionNumber ? project.version : nothing, tree_hash = pkg.tree_hash, path = pkg.path, repo = pkg.repo, uuid = pkg.uuid) manifest.deps[pkg.uuid] = entry _resolve(manifest, pkg.name) @@ -258,8 +259,8 @@ function develop(pkg::PackageSpec) pkg.repo.source = nothing end - - entry = PackageEntry(; apps = project.apps, name = pkg.name, version = project.version, tree_hash = pkg.tree_hash, path = sourcepath, repo = pkg.repo, uuid = pkg.uuid) + # PackageEntry requires version::Union{VersionNumber, Nothing}, but project.version can be VersionSpec + entry = PackageEntry(; apps = project.apps, name = pkg.name, version = project.version isa VersionNumber ? project.version : nothing, tree_hash = pkg.tree_hash, path = sourcepath, repo = pkg.repo, uuid = pkg.uuid) manifest = ctx.env.manifest manifest.deps[pkg.uuid] = entry diff --git a/src/Operations.jl b/src/Operations.jl index 3cf9c77fe5..bf0c9b750f 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -345,7 +345,12 @@ function update_manifest!(env::EnvCache, pkgs::Vector{PackageSpec}, deps_map, ju # Build package entries for pkg in pkgs entry = PackageEntry(; - name = pkg.name, version = pkg.version, pinned = pkg.pinned, + name = pkg.name, + # PackageEntry requires version::Union{VersionNumber, Nothing} + # pkg.version may be a VersionSpec in some cases (e.g., when freeing a package) + # so we convert non-VersionNumber values to nothing + version = pkg.version isa VersionNumber ? pkg.version : nothing, + pinned = pkg.pinned, tree_hash = pkg.tree_hash, path = pkg.path, repo = pkg.repo, uuid = pkg.uuid ) if is_stdlib(pkg.uuid, julia_version)