{"record":{"id":"1c86bb2624495651","repo":"jackwener/OpenCLI","slug":"fetch-error-1c86bb","errorCode":"FETCH_ERROR","errorMessage":"No candidate endpoint returned JSON","messagePattern":"No candidate endpoint returned JSON","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/zsxq/utils.js","lineNumber":129,"sourceCode":"          ok: false,\n          url: path,\n          error: error instanceof Error ? error.message : String(error),\n        };\n      }\n    })()\n  `);\n}\nexport async function fetchFirstJson(page, paths) {\n    let lastFailure = null;\n    for (const path of paths) {\n        const result = await browserJsonRequest(page, path);\n        if (result.ok) {\n            return result;\n        }\n        lastFailure = result;\n    }\n    if (!lastFailure) {\n        throw new CliError('FETCH_ERROR', 'No candidate endpoint returned JSON', `Checked endpoints: ${paths.join(', ')}`);\n    }\n    throw new CliError('FETCH_ERROR', lastFailure.error || 'Failed to fetch ZSXQ API', `Checked endpoints: ${paths.join(', ')}`);\n}\nexport function unwrapRespData(payload) {\n    const record = asRecord(payload);\n    if (!record) {\n        throw new CliError('PARSE_ERROR', 'Invalid ZSXQ API response');\n    }\n    if (record.succeeded === false) {\n        const code = typeof record.code === 'number' ? String(record.code) : 'API_ERROR';\n        const message = typeof record.info === 'string'\n            ? record.info\n            : typeof record.error === 'string'\n                ? record.error\n                : 'ZSXQ API returned an error';\n        throw new CliError(code, message);\n    }\n    return (record.resp_data ?? record.data ?? payload);","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/zsxq/utils.js#L111-L147","documentation":"fetchFirstJson tries each candidate API path in order and returns the first one that returns parseable JSON. If every candidate fails in a way that produced no failure record at all (lastFailure stays null, e.g. the loop body never produced a result), it throws CliError FETCH_ERROR 'No candidate endpoint returned JSON' with the checked endpoint list. It guards against silently returning nothing when all endpoint probes are inconclusive.","triggerScenarios":"All candidate endpoints exhausted without any result object being recorded — e.g. paths array semantics changed, every probe aborted before producing lastFailure, or network layer returned non-JSON that was discarded without setting lastFailure.","commonSituations":"ZSXQ changed/removed legacy API paths so none of the candidates respond; a corporate proxy or offline network kills requests before a JSON parse; auth redirect returns HTML on every endpoint.","solutions":["Check network connectivity and that api.zsxq.com is reachable from the browser session","Log in again (expired auth often causes HTML redirects instead of JSON)","Inspect the 'Checked endpoints:' detail and test those URLs manually in the logged-in browser","Update the CLI if ZSXQ changed its API endpoints"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  resp = await fetchFirstJson(page, paths);\n} catch (e) {\n  if (e.code === 'FETCH_ERROR' && /No candidate endpoint/.test(e.message)) {\n    console.error('All endpoints unreachable:', e.detail);\n  } else throw e;\n}","preventionTips":["Check network/proxy health before batch runs","Confirm auth is valid — HTML redirects masquerade as no-JSON responses","Keep endpoint candidate lists updated when ZSXQ changes its API"],"tags":["network","api","json","fetch"],"backgroundTag":"api-endpoint-no-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}