{"record":{"id":"4ec33b94fa8034e4","repo":"jackwener/OpenCLI","slug":"archive-search-returned-malformed-json-error-m","errorCode":null,"errorMessage":"archive search returned malformed JSON: ${error?.message || error}","messagePattern":"archive search returned malformed JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/archive/search.js","lineNumber":82,"sourceCode":"        let resp;\n        try {\n            resp = await fetch(url, {\n                headers: {\n                    'Accept': 'application/json',\n                    'User-Agent': 'opencli/1.0 (+https://github.com/jackwener/opencli)',\n                },\n            });\n        } catch (error) {\n            throw new CommandExecutionError(`archive search request failed: ${error?.message || error}`);\n        }\n        if (!resp.ok) {\n            throw new CommandExecutionError(`archive search failed: HTTP ${resp.status}`);\n        }\n        let data;\n        try {\n            data = await resp.json();\n        } catch (error) {\n            throw new CommandExecutionError(`archive search returned malformed JSON: ${error?.message || error}`);\n        }\n\n        const docs = data?.response?.docs;\n        if (!Array.isArray(docs)) {\n            throw new CommandExecutionError('archive search returned malformed payload: response.docs must be an array');\n        }\n        if (docs.length === 0) {\n            throw new EmptyResultError('archive search', `No items match \"${query}\" on archive.org.`);\n        }\n\n        return docs.slice(0, limit).map((d, i) => {\n            const id = String(d.identifier ?? '');\n            if (!IDENTIFIER_RE.test(id)) {\n                throw new CommandExecutionError('archive search returned malformed payload: result row is missing a stable identifier');\n            }\n            const downloads = Number(d.downloads ?? 0);\n            if (!Number.isFinite(downloads)) {\n                throw new CommandExecutionError(`archive search returned malformed payload for \"${id}\": downloads must be numeric`);","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/archive/search.js#L64-L100","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check what the endpoint actually returns: `curl -s 'https://archive.org/advancedsearch.php?q=test&output=json' | head`","Disable/bypass intercepting proxies or complete captive-portal login, then retry","Retry — truncated bodies are often transient","Catch this error and treat it as a network-layer failure with backoff, not a data problem"],"exampleFix":"// before\nconst data = JSON.parse(body); // throws raw SyntaxError on HTML body\n// after\nlet data;\ntry {\n    data = JSON.parse(body);\n} catch (err) {\n    throw new Error(`archive search returned malformed JSON: ${err.message}`);\n}","handlingStrategy":"try-catch","validationCode":"// pre-check the endpoint returns JSON in your environment\nconst probe = await fetch('https://archive.org/advancedsearch.php?q=test&output=json');\nconst ct = probe.headers.get('content-type') || '';\nif (!ct.includes('json')) throw new Error('endpoint not returning JSON; check proxy/captive portal');","typeGuard":"const isJsonObject = (text) => { try { return typeof JSON.parse(text) === 'object'; } catch { return false; } };","tryCatchPattern":"try {\n    rows = await run(['archive', 'search', query]);\n} catch (err) {\n    if (err instanceof CommandExecutionError && err.message.includes('malformed JSON')) {\n        // treat as network-layer failure: retry with backoff or surface clearly\n        await new Promise(r => setTimeout(r, 1000));\n        rows = await run(['archive', 'search', query]);\n    } else { throw err; }\n}","preventionTips":["Complete captive-portal login before running batch searches","Bypass or configure SSL-inspection proxies for archive.org","Fetch the endpoint once with curl to verify JSON content-type in your environment","Treat 200-with-HTML as a network environment problem, not a data problem"],"tags":["json","parsing","network","archive-org","cli"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}