{"record":{"id":"283ffff85fffa089","repo":"jackwener/OpenCLI","slug":"failed-to-fetch-chess-com-api-url-error-mes","errorCode":null,"errorMessage":"Failed to fetch Chess.com API ${url}: ${error?.message || error}","messagePattern":"Failed to fetch Chess\\.com API (.+?): (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"critical","filePath":"clis/chess/utils.js","lineNumber":49,"sourceCode":"    const s = String(value ?? '').trim();\n    if (!s) throw new ArgumentError('<game-url> is required');\n    const m = s.match(GAME_URL_RE);\n    if (!m) {\n        throw new ArgumentError(\n            `Invalid Chess.com game URL: \"${value}\"`,\n            'Expected https://www.chess.com/game/live/<id> or https://www.chess.com/game/daily/<id>.',\n        );\n    }\n    return { kind: m[1].toLowerCase(), id: m[2] };\n}\n\nexport async function chessApi(path, fetchImpl = fetch) {\n    const url = path.startsWith('http') ? path : `${API_BASE}${path}`;\n    let resp;\n    try {\n        resp = await fetchImpl(url, { headers: { 'User-Agent': UA, accept: 'application/json' } });\n    } catch (error) {\n        throw new CommandExecutionError(`Failed to fetch Chess.com API ${url}: ${error?.message || error}`);\n    }\n    if (!resp || typeof resp !== 'object') {\n        throw new CommandExecutionError(`Chess.com API returned an invalid response object for ${url}`);\n    }\n    if (resp.status === 404) throw new EmptyResultError(`Chess.com returned 404 for ${url}`);\n    if (!resp.ok) throw new CommandExecutionError(`Chess.com API returned HTTP ${resp.status} for ${url}`);\n    let payload;\n    try {\n        payload = await resp.json();\n    } catch (error) {\n        throw new CommandExecutionError(`Chess.com API returned malformed JSON for ${url}: ${error?.message || error}`);\n    }\n    if (!isPlainObject(payload)) {\n        throw new CommandExecutionError(`Chess.com API returned an unexpected payload shape for ${url}`);\n    }\n    return payload;\n}\n","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/chess/utils.js#L31-L67","documentation":"A CommandExecutionError thrown when the fetch itself rejects — the HTTP request to the Chess.com API never completed. The original error message is embedded, and the failing URL is included for diagnosis. This happens before any HTTP status can be inspected.","triggerScenarios":"DNS resolution failure for api.chess.com, connection refused/timeout, TLS errors, offline network, or a custom fetchImpl injected into chessApi that throws (e.g. a stub not configured or a proxy rejecting the request).","commonSituations":"Corporate proxy or firewall blocking api.chess.com; no internet connection; Node without a global fetch (older Node <18, so fetch is undefined and calling it throws); VPN or DNS misconfiguration.","solutions":["Check network connectivity and confirm https://api.chess.com is reachable (curl the URL).","On Node <18, polyfill global fetch or upgrade Node so the default fetchImpl exists.","Configure HTTP(S)_PROXY / agent settings if a corporate proxy is required.","If you inject a custom fetchImpl, verify it is a working async function returning a Response-like object."],"exampleFix":"// before\nconst stats = await chessApi('/player/hikaru/stats'); // Node 16: fetch is undefined\n// after\nimport fetch from 'node-fetch';\nglobalThis.fetch ??= fetch;\nconst stats = await chessApi('/player/hikaru/stats');","handlingStrategy":"retry","validationCode":"// Reachability pre-check before running the workflow:\nconst ok = await fetch('https://api.chess.com/pub/player/hikaru', { method: 'HEAD' })\n  .then(() => true)\n  .catch(() => false);\nif (!ok) throw new Error('api.chess.com unreachable; check network/proxy');","typeGuard":"function isNetworkError(e) {\n  return e instanceof Error && /Failed to fetch Chess.com API/.test(e.message);\n}","tryCatchPattern":"async function withRetry(fn, { retries = 3, backoffMs = 1000 } = {}) {\n  for (let i = 0; ; i++) {\n    try { return await fn(); }\n    catch (e) {\n      if (!isNetworkError(e) || i >= retries) throw e;\n      await new Promise((r) => setTimeout(r, backoffMs * 2 ** i));\n    }\n  }\n}\nconst stats = await withRetry(() => chessApi('/player/hikaru/stats'));","preventionTips":["Verify DNS/proxy reachability of api.chess.com in the deployment environment.","Ensure Node >= 18 or polyfill global fetch.","Wrap all chessApi calls in a retry-with-backoff helper.","If injecting fetchImpl, unit-test that it actually resolves to a Response."],"tags":["network","fetch","dns","timeout","chess-com"],"backgroundTag":"network-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}