{"record":{"id":"08a52a99a29dd04a","repo":"jackwener/OpenCLI","slug":"xiaoyuzhou-api-returned-invalid-json-geterrorme","errorCode":null,"errorMessage":"Xiaoyuzhou API returned invalid JSON: ${getErrorMessage(error)}","messagePattern":"Xiaoyuzhou API returned invalid JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaoyuzhou/auth.js","lineNumber":231,"sourceCode":"    }\n    let response = await performXiaoyuzhouJsonRequest(endpoint, options, credentials, fetchImpl);\n    if (response.status === 401) {\n        credentials = await refreshXiaoyuzhouCredentials(credentials, fetchImpl);\n        response = await performXiaoyuzhouJsonRequest(endpoint, options, credentials, fetchImpl);\n    }\n    const bodyText = await response.text();\n    if (!response.ok) {\n        if (response.status === 401 || response.status === 403) {\n            throw createXiaoyuzhouAuthError(`Xiaoyuzhou API rejected the credentials with HTTP ${response.status}`);\n        }\n        throw new CommandExecutionError(`Xiaoyuzhou API request failed with HTTP ${response.status}${bodyText ? `: ${bodyText}` : ''}`);\n    }\n    let parsed;\n    try {\n        parsed = JSON.parse(bodyText);\n    }\n    catch (error) {\n        throw new CommandExecutionError(`Xiaoyuzhou API returned invalid JSON: ${getErrorMessage(error)}`);\n    }\n    const serviceCode = parsed?.code;\n    if (serviceCode !== undefined && serviceCode !== null) {\n        const numericCode = Number(serviceCode);\n        if (!Number.isFinite(numericCode)) {\n            throw new CommandExecutionError('Xiaoyuzhou API returned an invalid service code');\n        }\n        if (numericCode === 401 || numericCode === 403) {\n            throw createXiaoyuzhouAuthError(`Xiaoyuzhou API rejected the credentials with service code ${numericCode}`);\n        }\n        if (numericCode !== 0 && numericCode !== 200) {\n            throw new CommandExecutionError(\n                parsed?.message || parsed?.msg || `Xiaoyuzhou API returned service code ${numericCode}`,\n            );\n        }\n    }\n    if (parsed?.success === false) {\n        throw new CommandExecutionError(parsed?.message || parsed?.msg || 'Xiaoyuzhou API returned success=false');","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoyuzhou/auth.js#L213-L249","documentation":"requestXiaoyuzhouJson calls JSON.parse on the raw response body from the Xiaoyuzhou (小宇宙) podcast API. If the body is not syntactically valid JSON (JSON.parse throws), this CommandExecutionError is thrown wrapping the parse error message. The CLI requires structured JSON responses to read code/data fields, so non-JSON payloads cannot be handled.","triggerScenarios":"Any of the callers (result, response, historyResponse, progressResponse, episodeResponse, transcriptResponse) receives a body that fails JSON.parse — e.g. an HTML error/login page, a Cloudflare/WAF block page, a 502 gateway HTML page, empty body, or truncated response.","commonSituations":"Reverse proxy or CDN intercepting the request and returning an HTML block page; server returning empty 502/504 bodies; cookies/expired token causing a redirect to an HTML login page; rate limiting returning plain text; wrong API base URL pointing at a non-JSON endpoint.","solutions":["Log the raw bodyText (and HTTP status) on failure to see what the server actually returned instead of JSON.","Check authentication: an expired token often yields HTML redirects; re-authenticate with clis/xiaoyuzhou auth before retrying.","Verify the API base URL and endpoint paths are correct and not hitting a CDN/WAF block page.","Retry after a delay if the server was temporarily returning gateway error pages (502/504).","Check network path (VPN, corporate proxy) that may inject HTML into responses."],"exampleFix":"// before: error hides the payload\nthrow new CommandExecutionError(`Xiaoyuzhou API returned invalid JSON: ${getErrorMessage(error)}`);\n// after: include status and a body snippet for diagnosis\nthrow new CommandExecutionError(`Xiaoyuzhou API returned invalid JSON: ${getErrorMessage(error)}; status=${response.status}; body=${bodyText.slice(0, 200)}`);","handlingStrategy":"try-catch","validationCode":"// before calling: check the response looks like JSON\nconst contentType = response.headers.get('content-type') || '';\nif (!contentType.includes('application/json') && !/^\\s*[{[]/.test(bodyText)) {\n  throw new Error(`Expected JSON, got content-type=${contentType}, body starts: ${bodyText.slice(0, 80)}`);\n}","typeGuard":"function isJsonObject(v) {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n// after parsing: if (!isJsonObject(parsed)) throw ...","tryCatchPattern":"try {\n  const result = await requestXiaoyuzhouJson(creds, path, params);\n} catch (e) {\n  if (e.message.startsWith('Xiaoyuzhou API returned invalid JSON')) {\n    // log status + raw body, treat as transient network/CDN issue; retry with backoff\n  } else throw e;\n}","preventionTips":["Log the HTTP status and first ~200 bytes of every non-JSON response for diagnosis.","Check the content-type header before parsing.","Keep auth fresh — expired tokens often redirect to HTML pages.","Bypass or whitelist corporate proxies/VPNs that inject HTML into responses.","Validate the API base URL in configuration before requests."],"tags":["json","api-response","network","parsing"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}