{"record":{"id":"0ea7cc118a0b4545","repo":"jackwener/OpenCLI","slug":"xiaohongshu-creator-note-detail-signed-api-suff-0ea7cc","errorCode":null,"errorMessage":"xiaohongshu creator-note-detail: signed API ${suffix} returned a non-text body","messagePattern":"xiaohongshu creator-note-detail: signed API (.+?) returned a non-text body","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/creator-note-detail.js","lineNumber":309,"sourceCode":"        assertOptionalArray(payload, 'source', suffix);\n    }\n    if (endpoint.key === 'audienceSourceDetail') {\n        for (const key of ['gender', 'age', 'city', 'interest']) {\n            assertOptionalArray(payload, key, suffix);\n        }\n    }\n    return payload;\n}\nfunction parseCapturedJson(capture, endpoint) {\n    const suffix = endpoint.suffix;\n    if (!capture || typeof capture !== 'object') {\n        throw new CommandExecutionError(`xiaohongshu creator-note-detail: malformed capture for ${suffix}`);\n    }\n    if (capture.ok !== true) {\n        throw new CommandExecutionError(`xiaohongshu creator-note-detail: signed API ${suffix} returned HTTP ${capture.status ?? 'non-2xx'}`);\n    }\n    if (typeof capture.body !== 'string') {\n        throw new CommandExecutionError(`xiaohongshu creator-note-detail: signed API ${suffix} returned a non-text body`);\n    }\n    try {\n        const envelope = JSON.parse(capture.body);\n        const payload = isPlainObject(envelope) && Object.hasOwn(envelope, 'data') ? envelope.data : envelope;\n        return validateCapturedPayload(payload, endpoint);\n    }\n    catch {\n        throw new CommandExecutionError(`xiaohongshu creator-note-detail: signed API ${suffix} returned invalid JSON or payload shape`);\n    }\n}\n// Capture the dashboard's signed datacenter/note responses on window.__xhsCapture\n// since a direct fetch() from page.evaluate bypasses the x-s signing and gets 406.\nasync function installXhsFetchCaptureHook(page) {\n    await page.evaluate(`(() => {\n    const targetPaths = ${JSON.stringify(DETAIL_API_ENDPOINTS.map((endpoint) => endpoint.suffix))};\n    const shouldCapture = (url) => {\n      try {\n        return targetPaths.includes(new URL(String(url), window.location.origin).pathname);","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/creator-note-detail.js#L291-L327","documentation":"parseCapturedJson throws this CommandExecutionError when a captured signed API response has capture.ok === true but capture.body is not a string. The fetch/XHR hook always stores body as text (resp.clone().text() / responseText), so a non-string body means the capture object was never populated by the hook or was corrupted — the library cannot safely JSON.parse it.","triggerScenarios":"window.__xhsCapture[url] exists for the endpoint suffix but its body field is undefined or non-string — e.g. the clone().text() promise rejected before body was set, an interception/response rewrite stripped the body, or an object at that URL key was written by something other than the capture hook.","commonSituations":"Another browser extension or injected script overwrote window.__xhsCapture; the response body was consumed/blocked before cloning (streams, service workers); running against a page version where the hook's text() silently fails; reusing a stale tab whose hook state is inconsistent.","solutions":["Close and reopen the browser tab (or restart the CLI run) so installXhsFetchCaptureHook resets window.__xhsCapture = {} and reinstalls a clean fetch/XHR hook.","Disable browser extensions or automation overlays that may tamper with fetch/XHR responses on creator.xiaohongshu.com.","Retry the command; a transient body-read failure inside the hook leaves ok:true with no body on the next poll only if re-requested — a fresh SPA navigation re-fires the signed requests.","If it reproduces, verify the endpoint still returns a text body by opening the network tab and checking the datacenter/note response."],"exampleFix":"// before\nrun('xiaohongshu creator-note-detail', { 'note-id': id }); // throws on corrupt capture\n// after\ntry {\n  return await run('xiaohongshu creator-note-detail', { 'note-id': id });\n} catch (e) {\n  if (/non-text body/.test(e.message)) {\n    return await runWithFreshTab('xiaohongshu creator-note-detail', { 'note-id': id });\n  }\n  throw e;\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function hasTextBody(c) {\n  return typeof c === 'object' && c !== null && c.ok === true && typeof c.body === 'string' && c.body.length > 0;\n}","tryCatchPattern":"try {\n  return await run('xiaohongshu creator-note-detail', { 'note-id': id });\n} catch (e) {\n  if (/non-text body/.test(e.message)) return await runInFreshTab('xiaohongshu creator-note-detail', { 'note-id': id });\n  throw e;\n}","preventionTips":["Run the command in a clean tab without interfering extensions.","Never assign to window.__xhsCapture manually in your own evaluate scripts.","Reset the tab between runs so the hook reinitializes the buffer.","Retry once on this error — it is usually a one-off hook/read glitch.","Check service workers on the domain that may intercept response bodies."],"tags":["capture-hook","browser-automation","xiaohongshu","corrupt-response"],"backgroundTag":"capture-hook-corrupt-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}