#5992·verdaccio

[Bug]: Web UI "Error loading manifests" — all `/-/verdaccio/data/*` endpoints return 404

Author: mroosz-c24Created Jul 6, 2026Updated Sep 4, 2026
Labelsfeat: auth

What happened?

Affected versions: v6.2.0 – v6.7.4 (and v7 betas) Regression introduced by: #5399 (9e404c1dc)


What happens

Opening the web UI triggers GET /-/verdaccio/data/packages (and sibling endpoints). All of them return:

json
404 {"error":"File not found"}

The UI shows "Error loading manifests" and the package list is never rendered. No server-side error is logged — the request silently falls through to the catch-all handler in src/api/index.ts.

All web API endpoints are affected: /packages, /sidebar, /readme, /search, /login, /reset_password.


Root cause

The bug requires two conditions that were introduced in separate PRs:

Step 1 — PR #5090 (f2cc71cd2, 2025-02-23, shipped in v6.1.0)

src/api/web/api/utils.ts was created with wrapPath / wrapSecPath returning absolute paths that include the /-/verdaccio/ prefix:

bash
export function wrapPath(urlPath: string) {
  return `/-/verdaccio/data${urlPath}`;  // e.g. "/-/verdaccio/data/packages"
}
export function wrapSecPath(urlPath: string) {
  return `/-/verdaccio/sec${urlPath}`;
}

At this point the child router had no mount prefix, so routes still matched. ✅


Step 2 — PR #5399 (9e404c1dc, 2025-09-29, shipped in v6.2.0)

A refactor introduced webMiddleware() and mounted the web API child router at WebUrlsNamespace.endpoints ("/-/verdaccio/"):

// src/api/web/index.ts router.use(WebUrlsNamespace.endpoints, webAPIMiddleware(tokenMiddleware, webEndpointsApi)); // "/-/verdaccio/" ← Express strips this prefix before child-router matching

Express strips the mount prefix from req.url before passing the request to the child router. So wrapPath's absolute paths no longer match any registered route. ❌


The failure chain:

  1. Request: GET /-/verdaccio/data/packages
  2. webMiddleware mounts child router at "/-/verdaccio/" — Express strips the prefix
  3. Child router receives req.url = "/data/packages"
  4. Registered route is "/-/verdaccio/data/packages" → no match
  5. Falls through to app.get('/*', next(ErrorCode.getNotFound(...))) → 404

Minimal reproducer

const express = require('express'); // 4.22.2
const app = express();
const outer = express.Router();
const inner = express.Router();

// current behaviour of wrapPath()
inner.get('/-/verdaccio/data/packages', (req, res) => res.json({ ok: true }));

outer.use('/-/verdaccio/', inner); // WebUrlsNamespace.endpoints
app.use(outer);
app.use((req, res) => res.status(404).json({ error: 'File not found' }));

// GET /-/verdaccio/data/packages → 404
// inner only sees: /data/packages  (prefix was stripped by Express)

Fix

wrapPath and wrapSecPath must return paths relative to the mount prefix, omitting the /-/verdaccio/ segment that Express has already consumed:

bash
 export function wrapPath(urlPath: string) {
-  return `/-/verdaccio/data${urlPath}`;
+  return `/data${urlPath}`;
 }

 export function wrapSecPath(urlPath: string) {
-  return `/-/verdaccio/sec${urlPath}`;
+  return `/sec${urlPath}`;
 }

Note: WebUrlsNamespace.data = "/data/" and WebUrlsNamespace.sec = "/sec/" already exist in @verdaccio/middleware and express exactly these relative paths — wrapPath/wrapSecPath could use them directly.


Affected versions

v6.0.x and earlier │ ✅ Unaffected — wrapPath does not exist v6.1.x │ ✅ Unaffected — absolute paths present but no mount prefix yet v6.2.0 – v6.7.4 │ ❌ Broken — both conditions present v7.0.0-beta.* │ ❌ Broken — same code

Version

6.x (Stable)

Version details

6.7.4

Output server log info

bash

Node.js Version

24.x.x (LTS)

Package manager

npm

Operating system

linux

Using reverse proxy

  • I am using a reverse proxy

Relevant log output

bash

Have I checked other issues?

  • I have searched existing issues