{"record":{"id":"6b1d84e5cf197a39","repo":"jackwener/OpenCLI","slug":"label-returned-http-429-rate-limited-6b1d84","errorCode":null,"errorMessage":"${label} returned HTTP 429 (rate limited)","messagePattern":"(.+?) returned HTTP 429 \\(rate limited\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":429,"severity":"warning","filePath":"clis/steam/utils.js","lineNumber":66,"sourceCode":"            'Copy the numeric id from `steam search` or the URL `store.steampowered.com/app/<id>/`.',\n        );\n    }\n    return s;\n}\n\nexport async function steamFetch(url, label) {\n    let resp;\n    try {\n        resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } });\n    }\n    catch (err) {\n        throw new CommandExecutionError(\n            `${label} request failed: ${err?.message ?? err}`,\n            'Check that store.steampowered.com is reachable from this network.',\n        );\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(\n            `${label} returned HTTP 429 (rate limited)`,\n            'Steam throttles bursty traffic; wait a few seconds and retry.',\n        );\n    }\n    if (resp.status === 404) {\n        throw new EmptyResultError(label, 'Steam returned 404 — the resource does not exist.');\n    }\n    if (!resp.ok) {\n        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);\n    }\n    let body;\n    try {\n        body = await resp.json();\n    }\n    catch (err) {\n        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);\n    }\n    return body;","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/steam/utils.js#L48-L84","documentation":"steamFetch detects HTTP 429 from Steam and throws a CommandExecutionError indicating the operation was rate limited, advising a short wait. Steam throttles bursty traffic against its store endpoints.","triggerScenarios":"Hitting store.steampowered.com endpoints repeatedly in a tight loop: batch-fetching many app ids, re-running search commands rapidly, or sharing an IP with other Steam API traffic.","commonSituations":"Batch scripts iterating hundreds of app ids without delay, CI jobs with frequent Steam lookups, office networks where many users share one egress IP, retry storms after earlier failures.","solutions":["Wait a few seconds and retry the same command","Add a delay/backoff between successive Steam requests in batch scripts","Reduce batch size or spread lookups over time","Cache results locally to avoid repeat requests for the same id"],"exampleFix":"// before\nfor (const id of ids) await steamFetch(appUrl(id), 'app details'); // bursts trigger 429\n// after\nfor (const id of ids) {\n  await steamFetch(appUrl(id), 'app details');\n  await new Promise(r => setTimeout(r, 1000));\n}","handlingStrategy":"retry","validationCode":"// no pre-check possible; mitigate with throttling\nconst sleep = (ms) => new Promise(r => setTimeout(r, ms));\nawait sleep(1000); // between consecutive steamFetch calls","typeGuard":"null","tryCatchPattern":"try { return await steamFetch(url, label); } catch (e) { if (/429/.test(e.message)) { await sleep(5000); return steamFetch(url, label); } throw e; }","preventionTips":["Throttle batch requests with delays of at least ~1s","Cache Steam responses to avoid repeat lookups","Use exponential backoff on 429","Avoid shared datacenter IPs when possible"],"tags":["http","rate-limit","steam"],"backgroundTag":"http-429-rate-limited","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}