{"record":{"id":"ea481aeee07686bc","repo":"jackwener/OpenCLI","slug":"label-returned-a-malformed-envelope","errorCode":null,"errorMessage":"${label} returned a malformed envelope","messagePattern":"(.+?) returned a malformed envelope","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/nowcoder/posts.js","lineNumber":251,"sourceCode":"    const number = Number(value);\n    if (!Number.isInteger(number) || number < 1 || number > maximum) throw new ArgumentError(`nowcoder --${name} must be an integer from 1 to ${maximum}`);\n    return number;\n}\n\nexport async function fetchNowcoderData(page, url, options, label) {\n    let payload;\n    try {\n        await page.goto('https://www.nowcoder.com');\n        payload = await page.fetchJson(url, options);\n    }\n    catch (error) {\n        const detail = String(error?.message ?? error);\n        if (/HTTP\\s+(401|403)|need login|not logged in/i.test(detail)) {\n            throw new AuthRequiredError('nowcoder.com', `${label} requires a logged-in Nowcoder session`);\n        }\n        throw new CommandExecutionError(`${label} failed: ${detail}`);\n    }\n    if (!isRecord(payload) || typeof payload.success !== 'boolean' || !Number.isSafeInteger(payload.code)) throw new CommandExecutionError(`${label} returned a malformed envelope`);\n    const message = typeof payload.msg === 'string' ? payload.msg : 'unknown error';\n    if (!payload.success || payload.code !== 0) {\n        if (payload.code === 999 || /need login|登录/i.test(message)) {\n            throw new AuthRequiredError('nowcoder.com', `${label} requires a logged-in Nowcoder session: ${message}`);\n        }\n        throw new CommandExecutionError(`${label} failed: ${message} (${payload.code})`);\n    }\n    if (!isRecord(payload.data)) throw new CommandExecutionError(`${label} returned malformed data`);\n    return payload.data;\n}\n","sourceCodeStart":233,"sourceCodeEnd":262,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/nowcoder/posts.js#L233-L262","documentation":"Nowcoder's JSON APIs wrap results in an envelope containing success (boolean), code (integer), and msg fields. fetchNowcoderData validates this envelope shape after a successful HTTP response; if the payload is not a record, success is not a boolean, or code is not a safe integer, it throws this CommandExecutionError because the response does not follow the expected Nowcoder API contract.","triggerScenarios":"The endpoint returned HTML (anti-bot challenge or error page parsed as JSON), a JSON array instead of an object, or a differently-shaped JSON object without the success/code fields.","commonSituations":"Hitting a WAF/anti-crawler interstitial that returns HTML with 200; Nowcoder changing its envelope schema; requesting the wrong endpoint URL that returns a non-envelope response; cookies prompting an HTML login page instead of JSON.","solutions":["Retry — anti-bot interstitials are often transient; reduce request rate and add delays.","Ensure you're logged in (an HTML login response can masquerade as a malformed envelope).","Verify the endpoint URL used by the library is still current; update the library if Nowcoder changed the API shape.","Capture the raw response to inspect what was actually returned and confirm the diagnosis."],"exampleFix":"// before\n// rapid scraping triggers anti-bot HTML with HTTP 200\nawait Promise.all(ids.map(getPost));\n// after\n// throttle and retry with backoff\nfor (const id of ids) { await getPost(id); await sleep(3000); }","handlingStrategy":"retry","validationCode":null,"typeGuard":"function isNowcoderEnvelope(payload) {\n  return payload !== null && typeof payload === 'object' && !Array.isArray(payload)\n    && typeof payload.success === 'boolean' && Number.isSafeInteger(payload.code);\n}","tryCatchPattern":"try {\n  const data = await fetchNowcoderData(page, url, options, label);\n} catch (err) {\n  if (err instanceof CommandExecutionError && /malformed envelope/.test(err.message)) {\n    // usually an anti-bot HTML page with HTTP 200: back off and retry\n    await sleep(10000);\n    return fetchNowcoderData(page, url, options, label);\n  }\n  throw err;\n}","preventionTips":["Throttle and randomize request timing to avoid anti-bot interstitials.","Stay logged in — HTML login responses can masquerade as malformed envelopes.","Keep the library current in case Nowcoder changes the envelope contract.","Log the raw response once when this occurs to confirm what was returned."],"tags":["api-response","schema-validation","anti-bot"],"backgroundTag":"malformed-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}