{"record":{"id":"6c44a42d799cae00","repo":"jackwener/OpenCLI","slug":"http-resp-status-resp-statustext-from-urls","errorCode":null,"errorMessage":"HTTP ${resp.status} ${resp.statusText} from ${urls[i]}","messagePattern":"HTTP (.+?) (.+?) from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/pipeline/steps/fetch.ts","lineNumber":70,"sourceCode":"  const urlsJs = JSON.stringify(urls);\n  const methodJs = JSON.stringify(method);\n  return (await page.evaluate(`\n    async () => {\n      const urls = ${urlsJs};\n      const method = ${methodJs};\n      const headers = ${headersJs};\n      const concurrency = ${concurrency};\n\n      const results = new Array(urls.length);\n      let idx = 0;\n\n      async function worker() {\n        while (idx < urls.length) {\n          const i = idx++;\n          try {\n            const resp = await fetch(urls[i], { method, headers, credentials: \"include\" });\n            if (!resp.ok) {\n              throw new Error('HTTP ' + resp.status + ' ' + resp.statusText + ' from ' + urls[i]);\n            }\n            results[i] = await resp.json();\n          } catch (e) {\n            results[i] = { error: e instanceof Error ? e.message : String(e) };\n            // Note: getErrorMessage() is a Node.js utility — can't use it inside evaluate()\n          }\n        }\n      }\n\n      const workers = Array.from({ length: Math.min(concurrency, urls.length) }, () => worker());\n      await Promise.all(workers);\n      return results;\n    }\n  `)) as unknown[];\n}\n\nexport async function stepFetch(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown> {\n  const paramObject = isRecord(params) ? params : {};","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/pipeline/steps/fetch.ts#L52-L88","documentation":"fetchBatchInBrowser runs inside browser evaluate() context and fetches a list of URLs concurrently with a worker pool. When a response is not ok it throws a plain Error whose message embeds the status, statusText, and URL; the catch block stores that message in results[i] as { error: message } rather than rejecting. It is not a typed CliError because CliError/getErrorMessage are Node.js utilities unavailable inside evaluate().","triggerScenarios":"Any URL in the batch array returning a non-ok response (404, 401, 403, 429, 5xx) during fetch(urls[i], { method, headers, credentials: 'include' }). The failure is recorded per-index instead of aborting the whole batch.","commonSituations":"One dead or renamed endpoint among many batched URLs, cookies expired so credentials:'include' requests get 401, server rate-limiting the burst of parallel requests (429), CORS or auth failures specific to the browser context.","solutions":["Scan results for entries with an .error property and read the embedded status/URL to identify the failing endpoint","Fix or remove the failing URL from the batch list","Re-authenticate so the include-credentials cookie session is valid for 401/403","Reduce worker concurrency or add retry/backoff for 429/5xx responses"],"exampleFix":"// before\nconst results = await fetchBatchInBrowser(urls, { method: 'GET', headers });\nconsole.log(results[3].data); // undefined, results[3] = { error: 'HTTP 404 Not Found from ...' }\n// after\nconst results = await fetchBatchInBrowser(urls, { method: 'GET', headers });\nfor (const r of results) if (r.error) console.warn('failed:', r.error);","handlingStrategy":"validation","validationCode":"const bad = urls.filter(u => { try { new URL(u); return false; } catch { return true; } });\nif (bad.length) throw new Error('invalid batch urls: ' + bad.join(', '));","typeGuard":"function hasBatchErrors(results: unknown[]): results is Array<{ error: string }> {\n  return results.some(r => typeof r === 'object' && r !== null && 'error' in r);\n}","tryCatchPattern":"// Errors are captured per-item in results, not thrown:\nconst results = await fetchBatchInBrowser(urls, { method, headers });\nfor (const [i, r] of results.entries()) {\n  if (r && typeof r === 'object' && 'error' in r) {\n    console.warn(`batch item ${i} failed: ${r.error}`);\n  }\n}","preventionTips":["Pre-flight check each URL in the batch resolves and the session cookie is fresh","Cap worker concurrency and add backoff to avoid 429s from burst traffic","Always inspect results for { error } entries instead of assuming all succeeded","Keep browser-side code free of Node-only utilities (as the source comment notes)"],"tags":["http","network","browser","batch-fetch"],"backgroundTag":"http-non-2xx-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}