jackwener/OpenCLI · error · CommandExecutionError
Chess.com API returned an invalid response object for ${url}
Error message
Chess.com API returned an invalid response object for ${url} What it means
A CommandExecutionError thrown when the fetch resolves but the result is not a usable Response-like object (null, undefined, or a non-object). The library expects resp.status/resp.ok/resp.json() to exist, so it fails fast with the offending URL in the message.
Source
Thrown at clis/chess/utils.js:52
if (!m) {
throw new ArgumentError(
`Invalid Chess.com game URL: "${value}"`,
'Expected https://www.chess.com/game/live/<id> or https://www.chess.com/game/daily/<id>.',
);
}
return { kind: m[1].toLowerCase(), id: m[2] };
}
export async function chessApi(path, fetchImpl = fetch) {
const url = path.startsWith('http') ? path : `${API_BASE}${path}`;
let resp;
try {
resp = await fetchImpl(url, { headers: { 'User-Agent': UA, accept: 'application/json' } });
} catch (error) {
throw new CommandExecutionError(`Failed to fetch Chess.com API ${url}: ${error?.message || error}`);
}
if (!resp || typeof resp !== 'object') {
throw new CommandExecutionError(`Chess.com API returned an invalid response object for ${url}`);
}
if (resp.status === 404) throw new EmptyResultError(`Chess.com returned 404 for ${url}`);
if (!resp.ok) throw new CommandExecutionError(`Chess.com API returned HTTP ${resp.status} for ${url}`);
let payload;
try {
payload = await resp.json();
} catch (error) {
throw new CommandExecutionError(`Chess.com API returned malformed JSON for ${url}: ${error?.message || error}`);
}
if (!isPlainObject(payload)) {
throw new CommandExecutionError(`Chess.com API returned an unexpected payload shape for ${url}`);
}
return payload;
}
/** Pull rating + record fields out of a stats sub-object (`chess_rapid` etc). */
export function summarizeStats(stats, kind) {
const k = stats?.[kind];View on GitHub (pinned to 49907e53dc)
Solutions
- Ensure the default global fetch is used, or make your fetchImpl return a real Response (or Response-like object with status, ok, and json()).
- Fix test stubs to mirror the Response shape: { ok: true, status: 200, json: async () => ({...}) }.
- Avoid wrappers that return await resp.json() instead of the Response itself.
Example fix
// before
chessApi(path, async () => ({ chess_rapid: {} })); // not a Response
// after
chessApi(path, async () => new Response(JSON.stringify({ chess_rapid: {} }), { status: 200 })); Defensive patterns
Strategy: type-guard
Validate before calling
// Validate any custom fetchImpl before use:
function isValidFetchImpl(fn) {
return typeof fn === 'function';
} Type guard
function isResponseLike(resp) {
return resp != null && typeof resp === 'object'
&& typeof resp.status === 'number'
&& typeof resp.ok === 'boolean'
&& typeof resp.json === 'function';
} Try / catch
try {
const payload = await chessApi(path, myFetch);
} catch (e) {
if (/invalid response object/.test(e.message)) {
console.error('fetchImpl must return a Response-like object');
} else throw e;
} Prevention
- Never wrap chessApi with functions that unwrap the Response before returning it.
- Make test stubs implement status, ok, and json().
- Prefer returning new Response(...) in mocks.
When it happens
Trigger: A custom fetchImpl passed to chessApi that returns null/undefined or a plain value (e.g. returning parsed JSON instead of a Response), or a mocked fetch in tests with an incomplete stub.
Common situations: Test doubles like fetch: async () => ({ json: ... }) missing from the contract, or wrappers that unwrap the Response before returning it.
Related errors
- Failed to fetch Chess.com API ${url}: ${error?.message || er
- FETCH_ERROR
- archive search request failed: ${error?.message || error}
- archive wayback request failed: ${error?.message || error}
- ${label} request failed: ${err?.message ?? err}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/481a2884e589a62e.
Report an issue: GitHub.