jackwener/OpenCLI · error · CliError
FETCH_ERROR
FETCH_ERROR
Error message
No candidate endpoint returned JSON
What it means
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.
Source
Thrown at clis/zsxq/utils.js:129
ok: false,
url: path,
error: error instanceof Error ? error.message : String(error),
};
}
})()
`);
}
export async function fetchFirstJson(page, paths) {
let lastFailure = null;
for (const path of paths) {
const result = await browserJsonRequest(page, path);
if (result.ok) {
return result;
}
lastFailure = result;
}
if (!lastFailure) {
throw new CliError('FETCH_ERROR', 'No candidate endpoint returned JSON', `Checked endpoints: ${paths.join(', ')}`);
}
throw new CliError('FETCH_ERROR', lastFailure.error || 'Failed to fetch ZSXQ API', `Checked endpoints: ${paths.join(', ')}`);
}
export function unwrapRespData(payload) {
const record = asRecord(payload);
if (!record) {
throw new CliError('PARSE_ERROR', 'Invalid ZSXQ API response');
}
if (record.succeeded === false) {
const code = typeof record.code === 'number' ? String(record.code) : 'API_ERROR';
const message = typeof record.info === 'string'
? record.info
: typeof record.error === 'string'
? record.error
: 'ZSXQ API returned an error';
throw new CliError(code, message);
}
return (record.resp_data ?? record.data ?? payload);View on GitHub (pinned to 49907e53dc)
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
Defensive patterns
Strategy: retry
Try / catch
try {
resp = await fetchFirstJson(page, paths);
} catch (e) {
if (e.code === 'FETCH_ERROR' && /No candidate endpoint/.test(e.message)) {
console.error('All endpoints unreachable:', e.detail);
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- hf datasets request failed: ${error?.message || error}
- ${label} returned malformed JSON: ${err?.message ?? err}
- zsxq whoami failed: ${probe.detail}
- 12306 ${endpoint} returned an unexpected payload shape
- ${label} returned HTTP ${resp.status}: ${summarizeApiError(p
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/1c86bb2624495651.
Report an issue: GitHub.