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

  1. Ensure the default global fetch is used, or make your fetchImpl return a real Response (or Response-like object with status, ok, and json()).
  2. Fix test stubs to mirror the Response shape: { ok: true, status: 200, json: async () => ({...}) }.
  3. 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

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


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