jackwener/OpenCLI · error · CommandExecutionError
响应不是有效 JSON
Error message
响应不是有效 JSON
What it means
fetchKeJson got an HTTP 2xx response but res.json() failed, so the in-page fetch returns {__keErr:'parse'} and the helper throws CommandExecutionError('响应不是有效 JSON'). This usually means the endpoint returned HTML instead of JSON — most often a risk-control/captcha interstitial or a login page — so the hint points the user to check login state and retry later.
Source
Thrown at clis/ke/utils.js:92
*/
export async function fetchKeJson(page, url) {
const result = await page.evaluate(`(async () => {
const res = await fetch(${JSON.stringify(url)}, { credentials: 'include' });
if (!res.ok) return { __keErr: res.status };
try {
return await res.json();
} catch {
return { __keErr: 'parse' };
}
})()`);
const r = result;
if (r?.__keErr !== undefined) {
const code = r.__keErr;
if (code === 401 || code === 403) {
throw new AuthRequiredError('ke.com', '未登录或登录已过期,请先在浏览器中登录贝壳找房');
}
if (code === 'parse') {
throw new CommandExecutionError('响应不是有效 JSON', '可能触发了风控,请检查登录状态或稍后重试');
}
throw new CommandExecutionError(`HTTP ${code}`, '请检查网络连接或登录状态');
}
return result;
}
/**
* Build a ke.com city URL prefix. Default city is 'bj' (Beijing).
*/
export function cityUrl(city) {
return `https://${city}.ke.com`;
}
View on GitHub (pinned to 49907e53dc)
Solutions
- Open the exact API URL in the logged-in browser and inspect the raw response
- If it's a captcha/risk page, complete verification in the browser, wait, and retry
- Re-login (ke auth login) to refresh the session
- Verify the endpoint URL and query params are correct
- Add request delays — repeated soft blocks indicate rate limiting
Example fix
// before
const r = await fetch(url, {credentials:'include'}); // 200 but HTML body -> __keErr:'parse'
// after
const r = await fetch(url, {credentials:'include', headers:{'Accept':'application/json'}});
const ct = r.headers.get('content-type') || '';
if (!ct.includes('json')) return { __keErr: 'parse' }; // fail fast with clearer signal Defensive patterns
Strategy: validation
Validate before calling
const r = await page.evaluate(`fetch(${JSON.stringify(url)}, {credentials:'include'}).then(async res => ({
status: res.status,
contentType: res.headers.get('content-type') || '',
head: (await res.text()).slice(0, 200)
}))`);
if (!r.contentType.includes('json')) {
console.log('Non-JSON response (' + r.contentType + '):', r.head);
console.log('Likely a captcha/WAF page — complete verification in the browser');
} Type guard
function looksLikeJson(html) {
const t = html.trim();
return t.startsWith('{') || t.startsWith('[');
} Try / catch
try {
const data = await fetchKeJson(page, apiUrl);
} catch (e) {
if (/响应不是有效 JSON/.test(e.message)) {
console.error('Endpoint returned non-JSON (risk-control page?). ' +
'Verify login, solve any captcha, wait, and retry with lower frequency.');
await new Promise(r => setTimeout(r, 30_000));
return fetchKeJson(page, apiUrl); // single retry after cooldown
}
throw e;
} Prevention
- Send Accept: application/json and validate content-type before parsing
- Inspect the raw response body when a new endpoint starts failing
- Lower request rate — soft blocks often return 200 + HTML challenge pages
- Double-check endpoint URLs; typos redirect to the SPA shell (HTML, not JSON)
When it happens
Trigger: Endpoint returns an HTML page (captcha, WAF block, login redirect) with status 200; a proxy/gateway injects an error page; the URL is wrong and hits the site's SPA shell; CDN returns compressed/mangled content the page parser can't decode.
Common situations: Beike风控 soft-blocks a session with a 200 HTML challenge; API path typo redirects to the website homepage; corporate proxy rewriting responses.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- 12306 ${endpoint} returned an unexpected payload shape
- archive search returned malformed JSON: ${error?.message ||
- archive snapshots returned malformed JSON: ${error?.message
- archive wayback returned malformed JSON: ${error?.message ||
- Chess.com callback returned malformed JSON for ${url}: ${err
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/948efb7fb857059d.
Report an issue: GitHub.