✅ Expected Behavior
vlt query ':workspace > *:outdated(out-of-range)' should list the outdated
dependencies when the configured registry is an authenticated registry with a
path prefix, e.g. https://registry.vlt.io/vltpkg/npm/.
❌ Actual Behavior
Every registry-backed dependency fails. queueNode warns with the full
error-cause chain per node, so a workspace with 131 direct registry deps prints
131 stack traces to stderr and then returns [] on stdout:
Error: Could not retrieve registry versions
at queueNode2 (…/vlt/chunk-47IR6Z6C.js:6810:7)
…
[cause]: {
name: 'hono',
cause: Error: Failed to fetch packument
at retrieveRemoteVersions (…/vlt/chunk-47IR6Z6C.js:6770:11)
There are two independent bugs in
src/query/src/pseudo/outdated.ts,
and fixing either one alone still leaves the selector broken.
1. The request is unauthenticated
retrieveRemoteVersions uses a raw fetch and only sends Accept (the comment
says this is to avoid the RegistryClient disk cache). That also means no
authorization header, so an authenticated registry replies 401 → !response.ok
→ Failed to fetch packument. The token is present in the keychain
(~/.local/share/vlt/auth/keychain.json has an entry for
https://registry.vlt.io/vltpkg/npm), it's just never consulted:
$ curl -s -o /dev/null -w '%{http_code}\n' https://registry.vlt.io/vltpkg/npm/hono
401
$ curl -s -o /dev/null -w '%{http_code}\n' -H "authorization: Bearer …" https://registry.vlt.io/vltpkg/npm/hono
200
2. The packument URL drops the registry path prefix
const url = new URL(spec.registry)
url.pathname = `/${node.name}`
Assigning pathname throws away the registry's path prefix, so the request goes
to https://registry.vlt.io/hono instead of
https://registry.vlt.io/vltpkg/npm/hono. With a valid token that URL is a 404,
which the code turns into AbortError('Missing API'):
$ curl -s -o /dev/null -w '%{http_code}\n' -H "authorization: Bearer …" https://registry.vlt.io/hono
404
This is invisible for root-path registries such as registry.npmjs.org, and only
breaks mirrors/proxies served under a prefix.
Not workaroundable via --registry
Passing --registry=https://registry.npmjs.org/ doesn't help: the dep IDs in
vlt-lock.json embed the registry
(~https_c++registry.vlt.io+vltpkg+npm+~@hono+node-server@1.19.17~peer.23) and
hydrate(node.id, …) takes spec.registry from the ID, so the override is
ignored and the same failures occur.
🔄 Steps to Reproduce
- In a project whose
vlt.json sets
"config": { "registry": "https://registry.vlt.io/vltpkg/npm/" } (an
authenticated, path-prefixed registry), run vlt install.
vlt login so a token exists in the keychain for that registry.
- Run
vlt query ':workspace > *:outdated(out-of-range)'.
- Observe one
Could not retrieve registry versions stack trace per dependency
and an empty [] result.
💡 Suggested Fix
- Build the URL with the existing helpers instead of overwriting
pathname —
new URL(name, registryBase(spec.registry)), the idiom already used in
package-info/src/index.ts
(const pakuURL = new URL(name, registry)). registryBase /
normalizeRegistryKey in registry-client/src/auth.ts were added for exactly
this class of prefixed mirror (see 7ef2692, "security-archive: fix results for
npm mirrors"); :outdated() never got the same treatment.
- Send an
authorization header. @vltpkg/query is browser-safe (it imports
@vltpkg/dep-id/browser), so it can't pull in the keychain directly — the
natural approach is to add a token or a fetchPackument callback to
ParserState (which already carries signal and retries) and supply it from
the query command, which has a RegistryClient.
- Optionally, collapse the per-node warnings into a single summary; one broken
registry currently produces hundreds of stack traces.
A regression test covering an authenticated registry served under a path prefix
would cover both halves.
🌍 Environment
- OS: linux v24.18.0
- Node: v24.18.0
- vlt: 1.0.2
🔄 Frequency
Always - Happens every time
✅ Expected Behavior
vlt query ':workspace > *:outdated(out-of-range)'should list the outdateddependencies when the configured registry is an authenticated registry with a
path prefix, e.g.
https://registry.vlt.io/vltpkg/npm/.❌ Actual Behavior
Every registry-backed dependency fails.
queueNodewarns with the fullerror-cause chain per node, so a workspace with 131 direct registry deps prints
131 stack traces to stderr and then returns
[]on stdout:There are two independent bugs in
src/query/src/pseudo/outdated.ts,and fixing either one alone still leaves the selector broken.
1. The request is unauthenticated
retrieveRemoteVersionsuses a rawfetchand only sendsAccept(the commentsays this is to avoid the
RegistryClientdisk cache). That also means noauthorizationheader, so an authenticated registry replies401→!response.ok→
Failed to fetch packument. The token is present in the keychain(
~/.local/share/vlt/auth/keychain.jsonhas an entry forhttps://registry.vlt.io/vltpkg/npm), it's just never consulted:2. The packument URL drops the registry path prefix
Assigning
pathnamethrows away the registry's path prefix, so the request goesto
https://registry.vlt.io/honoinstead ofhttps://registry.vlt.io/vltpkg/npm/hono. With a valid token that URL is a404,which the code turns into
AbortError('Missing API'):This is invisible for root-path registries such as
registry.npmjs.org, and onlybreaks mirrors/proxies served under a prefix.
Not workaroundable via
--registryPassing
--registry=https://registry.npmjs.org/doesn't help: the dep IDs invlt-lock.jsonembed the registry(
~https_c++registry.vlt.io+vltpkg+npm+~@hono+node-server@1.19.17~peer.23) andhydrate(node.id, …)takesspec.registryfrom the ID, so the override isignored and the same failures occur.
🔄 Steps to Reproduce
vlt.jsonsets"config": { "registry": "https://registry.vlt.io/vltpkg/npm/" }(anauthenticated, path-prefixed registry), run
vlt install.vlt loginso a token exists in the keychain for that registry.vlt query ':workspace > *:outdated(out-of-range)'.Could not retrieve registry versionsstack trace per dependencyand an empty
[]result.💡 Suggested Fix
pathname—new URL(name, registryBase(spec.registry)), the idiom already used inpackage-info/src/index.ts(
const pakuURL = new URL(name, registry)).registryBase/normalizeRegistryKeyinregistry-client/src/auth.tswere added for exactlythis class of prefixed mirror (see 7ef2692, "security-archive: fix results for
npm mirrors");
:outdated()never got the same treatment.authorizationheader.@vltpkg/queryis browser-safe (it imports@vltpkg/dep-id/browser), so it can't pull in the keychain directly — thenatural approach is to add a token or a
fetchPackumentcallback toParserState(which already carriessignalandretries) and supply it fromthe
querycommand, which has aRegistryClient.registry currently produces hundreds of stack traces.
A regression test covering an authenticated registry served under a path prefix
would cover both halves.
🌍 Environment
🔄 Frequency
Always - Happens every time