jackwener/OpenCLI · error · CommandExecutionError

Xiaoyuzhou API returned invalid JSON: ${getErrorMessage(erro

Error message

Xiaoyuzhou API returned invalid JSON: ${getErrorMessage(error)}

What it means

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.

Source

Thrown at clis/xiaoyuzhou/auth.js:231

    }
    let response = await performXiaoyuzhouJsonRequest(endpoint, options, credentials, fetchImpl);
    if (response.status === 401) {
        credentials = await refreshXiaoyuzhouCredentials(credentials, fetchImpl);
        response = await performXiaoyuzhouJsonRequest(endpoint, options, credentials, fetchImpl);
    }
    const bodyText = await response.text();
    if (!response.ok) {
        if (response.status === 401 || response.status === 403) {
            throw createXiaoyuzhouAuthError(`Xiaoyuzhou API rejected the credentials with HTTP ${response.status}`);
        }
        throw new CommandExecutionError(`Xiaoyuzhou API request failed with HTTP ${response.status}${bodyText ? `: ${bodyText}` : ''}`);
    }
    let parsed;
    try {
        parsed = JSON.parse(bodyText);
    }
    catch (error) {
        throw new CommandExecutionError(`Xiaoyuzhou API returned invalid JSON: ${getErrorMessage(error)}`);
    }
    const serviceCode = parsed?.code;
    if (serviceCode !== undefined && serviceCode !== null) {
        const numericCode = Number(serviceCode);
        if (!Number.isFinite(numericCode)) {
            throw new CommandExecutionError('Xiaoyuzhou API returned an invalid service code');
        }
        if (numericCode === 401 || numericCode === 403) {
            throw createXiaoyuzhouAuthError(`Xiaoyuzhou API rejected the credentials with service code ${numericCode}`);
        }
        if (numericCode !== 0 && numericCode !== 200) {
            throw new CommandExecutionError(
                parsed?.message || parsed?.msg || `Xiaoyuzhou API returned service code ${numericCode}`,
            );
        }
    }
    if (parsed?.success === false) {
        throw new CommandExecutionError(parsed?.message || parsed?.msg || 'Xiaoyuzhou API returned success=false');

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log the raw bodyText (and HTTP status) on failure to see what the server actually returned instead of JSON.
  2. Check authentication: an expired token often yields HTML redirects; re-authenticate with clis/xiaoyuzhou auth before retrying.
  3. Verify the API base URL and endpoint paths are correct and not hitting a CDN/WAF block page.
  4. Retry after a delay if the server was temporarily returning gateway error pages (502/504).
  5. Check network path (VPN, corporate proxy) that may inject HTML into responses.

Example fix

// before: error hides the payload
throw new CommandExecutionError(`Xiaoyuzhou API returned invalid JSON: ${getErrorMessage(error)}`);
// after: include status and a body snippet for diagnosis
throw new CommandExecutionError(`Xiaoyuzhou API returned invalid JSON: ${getErrorMessage(error)}; status=${response.status}; body=${bodyText.slice(0, 200)}`);
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling: check the response looks like JSON
const contentType = response.headers.get('content-type') || '';
if (!contentType.includes('application/json') && !/^\s*[{[]/.test(bodyText)) {
  throw new Error(`Expected JSON, got content-type=${contentType}, body starts: ${bodyText.slice(0, 80)}`);
}

Type guard

function isJsonObject(v) {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}
// after parsing: if (!isJsonObject(parsed)) throw ...

Try / catch

try {
  const result = await requestXiaoyuzhouJson(creds, path, params);
} catch (e) {
  if (e.message.startsWith('Xiaoyuzhou API returned invalid JSON')) {
    // log status + raw body, treat as transient network/CDN issue; retry with backoff
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/08a52a99a29dd04a. Report an issue: GitHub.