jackwener/OpenCLI · error · CommandExecutionError

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

Error message

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

What it means

packagistFetch throws this when resp.json() fails to parse the HTTP response body. It means the server responded but the payload was not valid JSON (HTML error page, truncated body, redirect page). The underlying parse error message is appended for diagnosis.

Source

Thrown at clis/packagist/utils.js:83

    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, `Packagist returned 404 for ${url}.`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(
            `${label} returned HTTP 429 (rate limited)`,
            'Packagist throttles 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;
}

/** Trim "2026-05-05T17:32:01+00:00" → "2026-05-05T17:32:01Z" so timestamps are uniform. */
export function trimDate(value) {
    const s = String(value ?? '').trim();
    if (!s) return null;
    const noFrac = s.replace(/\.\d+/, '');
    return noFrac.replace(/(?:[+-]\d{2}:?\d{2}|Z)?$/, 'Z');
}

/**
 * Pick the newest stable (non-dev / non-prerelease) version key from a
 * Packagist `versions` map. Packagist returns keys ordered newest-first.
 * Falls back to the first key if no stable found.
 */
export function pickStableVersion(versions) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the request — transient truncation is common.
  2. Check whether a proxy/firewall is intercepting and rewriting responses (look for HTML in the raw response).
  3. Verify the API endpoint URL has not changed or been redirected.
  4. Inspect err.message in the thrown error for the specific JSON parse failure.
Defensive patterns

Strategy: retry

Try / catch

try {
  const data = await body(pkg);
} catch (err) {
  if (/malformed JSON/.test(err.message)) {
    // likely transient: retry with backoff
    await sleep(2000);
    return body(pkg);
  }
  throw err;
}

Prevention

When it happens

Trigger: Packagist or an intermediary returns HTML (error/captcha/redirect page) with 200, or the response body is truncated/corrupted mid-transfer.

Common situations: Captive portals or proxies injecting HTML into responses; CDN error pages; network interruption truncating the body; hitting a mirror that returns non-JSON.

Understand the failure class

Related errors


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