jackwener/OpenCLI · error · CommandExecutionError

archive wayback returned malformed JSON: ${error?.message ||

Error message

archive wayback returned malformed JSON: ${error?.message || error}

What it means

Thrown when the response body from the Wayback Machine save API cannot be parsed as JSON. The library expects the archived_snapshots envelope in the response; an HTML error page, empty body, or truncated response makes resp.json() throw, which is wrapped in a CommandExecutionError. It means the HTTP call succeeded but the body was not valid JSON.

Source

Thrown at clis/archive/wayback.js:64

        let resp;
        try {
            resp = await fetch(apiUrl, {
                headers: {
                    'Accept': 'application/json',
                    'User-Agent': 'opencli/1.0 (+https://github.com/jackwener/opencli)',
                },
            });
        } catch (error) {
            throw new CommandExecutionError(`archive wayback request failed: ${error?.message || error}`);
        }
        if (!resp.ok) {
            throw new CommandExecutionError(`archive wayback failed: HTTP ${resp.status}`);
        }
        let data;
        try {
            data = await resp.json();
        } catch (error) {
            throw new CommandExecutionError(`archive wayback returned malformed JSON: ${error?.message || error}`);
        }

        const snap = data?.archived_snapshots?.closest;
        if (!snap || !snap.available) {
            throw new EmptyResultError('archive wayback', `No Wayback snapshot for "${target}".`);
        }
        if (typeof snap.url !== 'string' || !snap.url || !/^\d{14}$/.test(String(snap.timestamp ?? ''))) {
            throw new CommandExecutionError('archive wayback returned malformed payload: closest snapshot is missing url/timestamp');
        }

        return [{
            original_url: String(data.url ?? target),
            requested_timestamp: timestamp,
            snapshot_timestamp: String(snap.timestamp ?? ''),
            snapshot_url: String(snap.url),
            status: String(snap.status ?? ''),
        }];
    },

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the command — Wayback sometimes returns transient non-JSON responses
  2. Inspect the raw response by fetching the save URL with curl to see what is actually returned
  3. Check whether a corporate proxy or VPN is injecting HTML into responses
  4. If persistent, check web.archive.org status; the service may be returning error pages with 200 status

Example fix

// before
await exec('opencli archive wayback ' + url);
// after
try {
  await exec('opencli archive wayback ' + url);
} catch (e) {
  if (e.message.includes('malformed JSON')) {
    console.error('Wayback returned non-JSON (possible HTML error page); retrying...');
    return await exec('opencli archive wayback ' + url);
  }
  throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try {
  await runWayback(url);
} catch (e) {
  if (e.message.includes('malformed JSON')) {
    await sleep(2000);
    return runWayback(url); // transient HTML error page — retry once
  }
  throw e;
}

Prevention

When it happens

Trigger: `opencli archive wayback <url>` succeeds at HTTP level but resp.json() throws — e.g. the server returned an HTML interstitial/anti-bot page, a redirect without a body, or a truncated response.

Common situations: Wayback serving an HTML captcha or maintenance page with 200 status; proxies/corporate gateways rewriting the response; intermittent network truncation.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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