agalwood/Motrix · error · Error

registry responded ${res.status}

Error message

registry responded ${res.status}

What it means

Thrown internally by RegistryClient.doRefresh when the registry HTTP response status is not 2xx and not 304 (res.ok is false). It is constructed as a plain Error whose message embeds the status code. Note: doRefresh catches this itself, logs 'registry refresh failed; keeping last-good', and returns the cached file or null — so callers of list()/get() normally observe stale/empty results rather than this Error directly.

Source

Thrown at src/core/plugin/registry/registry-client.ts:162

      }, FETCH_TIMEOUT_MS)
    })

    try {
      const headers: Record<string, string> = {}
      if (this.cache?.etag) headers['if-none-match'] = this.cache.etag

      const res = await Promise.race([
        this.fetchImpl(this.url, { headers, signal: controller.signal }),
        deadline,
      ])

      if (res.status === 304 && this.cache) {
        this.cache = { ...this.cache, fetchedAt: this.now() }
        await this.persist()
        return this.cache.file
      }
      if (!res.ok) {
        throw new Error(`registry responded ${res.status}`)
      }

      const encoded = await Promise.race([
        this.readBoundedResponse(res, controller),
        deadline,
      ])
      const raw: unknown = JSON.parse(encoded)
      const file = RegistryFileSchema.parse(raw)
      this.cache = {
        cacheFormat: 2,
        etag: res.headers.get('etag'),
        fetchedAt: this.now(),
        raw,
        file,
      }
      await this.persist()
      return file
    } catch (error) {

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Check the embedded status: 404 -> verify this.url points at the live registry file; 401/403 -> check auth headers/proxy; 5xx -> transient, rely on the cache and retry.
  2. Confirm network/proxy reachability to the registry host.
  3. For test code, ensure the fetchImpl stub returns `Response.json(..., { status: 200 })`.
  4. Handle the downstream effect: list()/get() may return stale or null — surface a 'registry unavailable, using cache' notice to the user.

Example fix

// before (test stub returning default 404)
const client = new RegistryClient({ url: 'https://reg/app.json', fetchImpl: () => Promise.resolve(new Response()) })

// after
const client = new RegistryClient({ url: 'https://reg/app.json', fetchImpl: () => Promise.resolve(new Response(JSON.stringify(file), { status: 200, headers: { 'content-type': 'application/json' } })) })
Defensive patterns

Strategy: fallback

Try / catch

// doRefresh already swallows this and returns cache?.file ?? null.
// Callers of list()/get() should treat null/empty as 'registry unavailable':
const entries = await client.list(hostVersion)
if (entries.length === 0) {
  notifyUser('Registry is unavailable; plugin list may be stale or empty.')
}

Prevention

When it happens

Trigger: doRefresh() fetches this.url; the response has res.ok === false (e.g. 404, 401, 500, 502) and is not a 304. The fetchImpl is the real network fetch (or a test stub). Proxy/CDN returns an error page.

Common situations: Registry endpoint is misconfigured or moved (404). Auth/CORS changed (401/403). Registry server down or behind a failing proxy (5xx). Custom fetchImpl in tests returns a non-ok Response.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/e1d5fdf6159fb0e7. Report an issue: GitHub.