{"record":{"id":"bfffe4f5a3880209","repo":"jackwener/OpenCLI","slug":"fetch-error","errorCode":"FETCH_ERROR","errorMessage":"FETCH_ERROR: Unable to reach Apple Podcasts charts for ${country.toUpperCase()}","messagePattern":"FETCH_ERROR: Unable to reach Apple Podcasts charts for (.+?)","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/apple-podcasts/top.js","lineNumber":30,"sourceCode":"    browser: false,\n    args: [\n        { name: 'limit', type: 'int', default: 20, help: 'Number of podcasts (max 100)' },\n        { name: 'country', default: 'us', help: 'Country code (e.g. us, cn, gb, jp)' },\n    ],\n    columns: ['rank', 'title', 'author', 'id'],\n    func: async (args) => {\n        const limit = Math.max(1, Math.min(Number(args.limit), 100));\n        const country = String(args.country || 'us').trim().toLowerCase();\n        const url = `${CHARTS_URL}/${country}/podcasts/top/${limit}/podcasts.json`;\n        let resp;\n        try {\n            resp = await fetch(url, {\n                signal: AbortSignal.timeout(CHARTS_TIMEOUT_MS),\n            });\n        }\n        catch (error) {\n            const reason = error?.cause?.code ?? error?.message ?? 'unknown network error';\n            throw new CliError('FETCH_ERROR', `Unable to reach Apple Podcasts charts for ${country.toUpperCase()}`, `Apple charts may be temporarily unavailable (${reason}). Try again later.`);\n        }\n        if (!resp.ok)\n            throw new CliError('FETCH_ERROR', `Charts API HTTP ${resp.status}`, `Check country code: ${country}`);\n        const data = await resp.json();\n        const results = data?.feed?.results;\n        if (!results?.length)\n            throw new CliError('NOT_FOUND', 'No chart data found', `Try a different country code`);\n        return results.map((p, i) => ({\n            rank: i + 1,\n            title: p.name,\n            author: p.artistName,\n            id: p.id,\n        }));\n    },\n});\n","sourceCodeStart":12,"sourceCodeEnd":46,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/apple-podcasts/top.js#L12-L46","documentation":"Thrown by `apple-podcasts top` as a CliError with code FETCH_ERROR when the HTTP request to Apple's charts RSS/API fails at the network level (fetch rejects before a response is received). The message includes the country and the underlying cause (error.cause.code or message) to help diagnose. It covers DNS failures, timeouts (AbortSignal.timeout), TLS errors, and connection resets.","triggerScenarios":"`opencli apple-podcasts top --country <cc>` where fetch(url, { signal: AbortSignal.timeout(CHARTS_TIMEOUT_MS) }) rejects — timeout exceeded, DNS failure, offline network, or proxy blocking itunes.apple.com.","commonSituations":"Offline or firewalled network; corporate proxy blocking Apple domains; charts endpoint temporarily down; CHARTS_TIMEOUT_MS too low on a slow connection.","solutions":["Check basic connectivity (e.g. curl https://itunes.apple.com) and retry after transient failures","Verify the timeout is adequate for your network or raise CHARTS_TIMEOUT_MS if configurable","Check proxy/VPN/firewall settings that may block itunes.apple.com; the error's reason field names the underlying code","Confirm Apple's charts service status; try a different country code to isolate a regional outage"],"exampleFix":"// before\nresp = await fetch(url, {});  // no timeout handling\n// after\ntry {\n  resp = await fetch(url, { signal: AbortSignal.timeout(CHARTS_TIMEOUT_MS) });\n} catch (error) {\n  // retry once, then surface CliError('FETCH_ERROR', ...) with error?.cause?.code\n}","handlingStrategy":"retry","validationCode":"// Cheap pre-flight: is the endpoint reachable at all?\nconst probe = await fetch('https://itunes.apple.com/', { signal: AbortSignal.timeout(5000) })\n  .then((r) => r.ok).catch(() => false);\nif (!probe) console.log('Network cannot reach itunes.apple.com; fix connectivity before running top.');","typeGuard":null,"tryCatchPattern":"try {\n  await cli.run(['apple-podcasts', 'top', '--country', 'us']);\n} catch (e) {\n  if (e.code === 'FETCH_ERROR' && /Unable to reach/.test(e.message)) {\n    await new Promise((r) => setTimeout(r, 2000));\n    // retry once or surface with e.hint's reason\n  } else throw e;\n}","preventionTips":["Run charts commands only when connectivity to itunes.apple.com is verified","Account for CHARTS_TIMEOUT_MS on slow links; prefer wired/stable networks in CI","Set proxy env vars correctly if behind a corporate firewall","Wrap in retry-with-backoff since the error text explicitly suggests trying later"],"tags":["network","fetch","timeout","apple-podcasts"],"backgroundTag":"network-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}