jackwener/OpenCLI · error · CommandExecutionError

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

Error message

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

What it means

This CommandExecutionError is thrown when the HTTP response from archive.org's advancedsearch endpoint has ok status but its body cannot be parsed as JSON (resp.json() rejects). The parse error message is interpolated. It means the body was HTML, empty, or truncated rather than valid JSON.

Source

Thrown at clis/archive/search.js:82

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

        const docs = data?.response?.docs;
        if (!Array.isArray(docs)) {
            throw new CommandExecutionError('archive search returned malformed payload: response.docs must be an array');
        }
        if (docs.length === 0) {
            throw new EmptyResultError('archive search', `No items match "${query}" on archive.org.`);
        }

        return docs.slice(0, limit).map((d, i) => {
            const id = String(d.identifier ?? '');
            if (!IDENTIFIER_RE.test(id)) {
                throw new CommandExecutionError('archive search returned malformed payload: result row is missing a stable identifier');
            }
            const downloads = Number(d.downloads ?? 0);
            if (!Number.isFinite(downloads)) {
                throw new CommandExecutionError(`archive search returned malformed payload for "${id}": downloads must be numeric`);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check what the endpoint actually returns: `curl -s 'https://archive.org/advancedsearch.php?q=test&output=json' | head`
  2. Disable/bypass intercepting proxies or complete captive-portal login, then retry
  3. Retry — truncated bodies are often transient
  4. Catch this error and treat it as a network-layer failure with backoff, not a data problem

Example fix

// before
const data = JSON.parse(body); // throws raw SyntaxError on HTML body
// after
let data;
try {
    data = JSON.parse(body);
} catch (err) {
    throw new Error(`archive search returned malformed JSON: ${err.message}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check the endpoint returns JSON in your environment
const probe = await fetch('https://archive.org/advancedsearch.php?q=test&output=json');
const ct = probe.headers.get('content-type') || '';
if (!ct.includes('json')) throw new Error('endpoint not returning JSON; check proxy/captive portal');

Type guard

const isJsonObject = (text) => { try { return typeof JSON.parse(text) === 'object'; } catch { return false; } };

Try / catch

try {
    rows = await run(['archive', 'search', query]);
} catch (err) {
    if (err instanceof CommandExecutionError && err.message.includes('malformed JSON')) {
        // treat as network-layer failure: retry with backoff or surface clearly
        await new Promise(r => setTimeout(r, 1000));
        rows = await run(['archive', 'search', query]);
    } else { throw err; }
}

Prevention

When it happens

Trigger: Calling `opencli archive search ...` when a captive portal or proxy returns an HTML login/block page with 200; archive.org serves an error page with 200; the response body is cut off mid-stream; a misbehaving middlebox rewrites content.

Common situations: Hotel/airport wifi captive portals intercepting the request; corporate SSL-inspection proxies injecting pages; extremely large responses truncated by an intermediary; archive.org returning an HTML rate-limit notice with 200.

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/4ec33b94fa8034e4. Report an issue: GitHub.