jackwener/OpenCLI · error · CommandExecutionError
Failed to reach Xiaoyuzhou API: ${getErrorMessage(error)}
Error message
Failed to reach Xiaoyuzhou API: ${getErrorMessage(error)} What it means
Thrown by performXiaoyuzhouJsonRequest when the fetch call to a Xiaoyuzhou API endpoint throws before an HTTP response exists — network unreachable, DNS failure, TLS error, or the 20-second AbortSignal.timeout firing. It is wrapped as CommandExecutionError; auth problems are intentionally NOT this error (those surface later as AUTH_REQUIRED CliErrors).
Source
Thrown at clis/xiaoyuzhou/auth.js:204
const {
method = 'GET',
query,
body,
} = options;
let response;
try {
response = await fetchImpl(buildApiUrl(endpoint, query), {
method,
headers: buildXiaoyuzhouHeaders(credentials, {
contentType: 'application/json',
includeLocalTime: true,
}),
body: body === undefined ? undefined : JSON.stringify(body),
signal: AbortSignal.timeout(20_000),
});
}
catch (error) {
throw new CommandExecutionError(`Failed to reach Xiaoyuzhou API: ${getErrorMessage(error)}`);
}
return response;
}
export async function requestXiaoyuzhouJson(endpoint, options = {}, fetchImpl = fetch) {
let credentials = options.credentials ?? loadXiaoyuzhouCredentials();
if (shouldRefreshXiaoyuzhouCredentials(credentials)) {
credentials = await refreshXiaoyuzhouCredentials(credentials, fetchImpl);
}
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}`);View on GitHub (pinned to 49907e53dc)
Solutions
- Verify reachability: curl -v https://api.xiaoyuzhoufm.com — fix connectivity, DNS, or firewall before retrying.
- If timeouts recur, pass a custom fetchImpl with a longer AbortSignal timeout.
- Add retry with exponential backoff for transient network errors around requestXiaoyuzhouJson calls.
- Confirm Node >= 18 so global fetch is available.
Example fix
// before: single attempt, hard failure on flaky network
const { data } = await requestXiaoyuzhouJson('/episodes/get', { query: { eid } });
// after: bounded retry on transport errors
async function fetchWithRetry(endpoint, options) {
for (let attempt = 0; ; attempt++) {
try { return await requestXiaoyuzhouJson(endpoint, options); }
catch (e) {
if (!String(e.message).includes('Failed to reach') || attempt >= 2) throw e;
await new Promise(r => setTimeout(r, 500 * 2 ** attempt));
}
}
} Defensive patterns
Strategy: retry
Validate before calling
// network preflight
async function assertApiReachable() {
try {
await fetch('https://api.xiaoyuzhoufm.com', { method: 'HEAD', signal: AbortSignal.timeout(5000) });
} catch (e) {
throw new Error(`Xiaoyuzhou API unreachable (${e.message}). Check network, DNS, proxy, and Node >= 18.`);
}
} Type guard
function isTransportError(err) {
return err instanceof Error && err.message.startsWith('Failed to reach Xiaoyuzhou API:');
} Try / catch
import { CommandExecutionError } from '@jackwener/opencli/errors';
async function withRetry(endpoint, options, attempts = 3) {
for (let i = 0; ; i++) {
try {
return await requestXiaoyuzhouJson(endpoint, options);
} catch (err) {
const transient = err instanceof CommandExecutionError
&& err.message.startsWith('Failed to reach Xiaoyuzhou API:');
if (!transient || i >= attempts - 1) throw err;
await new Promise(r => setTimeout(r, 500 * 2 ** i));
}
}
} Prevention
- Add exponential-backoff retry around all requestXiaoyuzhouJson calls for transient transport errors.
- Monitor DNS/connectivity in the deployment environment; allowlist api.xiaoyuzhoufm.com in firewalls.
- Use Node >= 18 so global fetch and AbortSignal.timeout are available.
- Prefer failing fast with a clear preflight reachability check over long hangs at the 20s timeout.
When it happens
Trigger: Any requestXiaoyuzhouJson call (episodes, transcripts, history, progress) where the TCP/TLS connection to api.xiaoyuzhoufm.com cannot be established or the request exceeds 20s: offline machine, DNS outage, firewall, or hung connection.
Common situations: No internet / Wi-Fi dropped; DNS misconfiguration; IPv6-only or broken dual-stack network; firewalled corporate network; slow VPN causing the 20s timeout; Node < 18 lacking global fetch (fetchImpl undefined → TypeError wrapped here).
Related errors
- archive wayback request failed: ${error?.message || error}
- Failed to fetch Chess.com API ${url}: ${error?.message || er
- FETCH_ERROR
- coingecko top request failed: ${error?.message || error}
- coingecko trending request failed: ${error?.message || error
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/3526fc7e28fbc6ca.
Report an issue: GitHub.