jackwener/OpenCLI · error · CommandExecutionError

${label} returned malformed JSON: ${err?.message ?? err}

Error message

${label} returned malformed JSON: ${err?.message ?? err}

What it means

After a successful HTTP response, npmFetch parses the body as JSON. If resp.json() throws — meaning the endpoint returned HTML, an error page, empty content, or truncated data instead of valid JSON — the helper wraps the parse failure in a CommandExecutionError noting the label and underlying parse message. This indicates the response did not come from a healthy JSON API endpoint.

Source

Thrown at clis/npm/utils.js:73

    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, `npm registry returned 404 for ${url}.`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(
            `${label} returned HTTP 429 (rate limited)`,
            'npm throttles unauthenticated bursts; wait a few seconds and retry.',
        );
    }
    if (!resp.ok) {
        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
    }
    let body;
    try {
        body = await resp.json();
    }
    catch (err) {
        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
    }
    return body;
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Fetch the same URL with `curl -i <url>` and inspect the raw body to see what is actually being returned (HTML page? empty? proxy notice?)
  2. Check whether a corporate proxy/captive portal is intercepting traffic and authenticate or bypass it
  3. Retry after a short delay — intermittent truncation from flaky networks resolves on retry
  4. Ensure requests go to the official endpoints (registry.npmjs.org, api.npmjs.org) and no overridden registry URL (npm config get registry) is mangling responses

Example fix

// before
const data = await npmFetch(url, 'npm package');
// after
try {
  const data = await npmFetch(url, 'npm package');
} catch (err) {
  if (String(err.message).includes('malformed JSON')) {
    console.error('Registry returned non-JSON content; check proxy/network or retry.');
  }
  throw err;
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try {
  return await npmFetch(url, label);
} catch (err) {
  if (/malformed JSON/.test(String(err.message))) {
    // Likely proxy/interstitial or truncated response — retry once
    await sleep(500);
    return await npmFetch(url, label);
  }
  throw err;
}

Prevention

When it happens

Trigger: The registry or an intermediary returns non-JSON content for a URL that npmFetch expected to be JSON: a captive-portal/HTML login page from a proxy, an npm incident serving an HTML error page with 200 status, a truncated gzip body from a flaky connection, or an empty body from a misbehaving cache/CDN edge.

Common situations: Corporate networks or VPNs injecting HTML interstitial pages; malformed responses from custom registry mirrors or cached proxies; DNS hijacking redirects; requests that hit api.npmjs.org rate-limit pages with unexpected content types.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/25d86a80051d1558. Report an issue: GitHub.