{"record":{"id":"ffd1a706b6c5fc9f","repo":"jackwener/OpenCLI","slug":"batch-fetch-failed-for-itemurl-message","errorCode":null,"errorMessage":"Batch fetch failed for ${itemUrl}: ${message}","messagePattern":"Batch fetch failed for (.+?): (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/pipeline/steps/fetch.ts","lineNumber":133,"sourceCode":"    if (page !== null) {\n      const results = await fetchBatchInBrowser(page, urls, method.toUpperCase(), renderedHeaders, concurrency);\n      for (let i = 0; i < results.length; i++) {\n        const r = results[i];\n        if (r && typeof r === 'object' && 'error' in r) {\n          log.warn(`Batch fetch failed for ${urls[i]}: ${(r as { error: string }).error}`);\n        }\n      }\n      return results;\n    }\n\n    // Non-browser: use concurrent pool (already optimized)\n    return mapConcurrent(data, concurrency, async (item, index) => {\n      const itemUrl = String(render(urlTemplate, { args, data, item, index }));\n      try {\n        return await fetchSingle(null, itemUrl, method, queryParams, headers, args, data);\n      } catch (error) {\n        const message = getErrorMessage(error);\n        log.warn(`Batch fetch failed for ${itemUrl}: ${message}`);\n        return { error: message };\n      }\n    });\n  }\n  const url = render(urlOrObj, { args, data });\n  return fetchSingle(page, String(url), method, queryParams, headers, args, data);\n}\n","sourceCodeStart":115,"sourceCodeEnd":141,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/pipeline/steps/fetch.ts#L115-L141","documentation":"In stepFetch's non-browser path, mapConcurrent runs fetchSingle per rendered URL. Any exception from fetchSingle (network error, timeout, HTTP failure) is caught, converted to a message via getErrorMessage, logged as this warning, and returned as { error: message } so one failing row doesn't abort the whole batch.","triggerScenarios":"fetchSingle throws for a data item — the rendered itemUrl is malformed (bad template interpolation), DNS/connection failure, request timeout, or the server returns a status fetchSingle treats as an error.","commonSituations":"Template placeholders missing from an item yielding 'undefined' in the URL; rate limiting (429) when concurrency is high; flaky third-party APIs; items built from user-supplied data with spaces/unsafe characters.","solutions":["Inspect the per-item message and encode/validate the rendered URL before fetching (encodeURI/URL constructor).","Lower mapConcurrent concurrency or add retry with backoff for rate-limited or flaky endpoints.","Add timeout configuration to fetchSingle so slow items fail fast and predictable.","Check items for missing fields before rendering (item.id etc.) and skip incomplete rows."],"exampleFix":"// before\nconst itemUrl = String(render(urlTemplate, { args, data, item, index }));\n// after\nconst rendered = render(urlTemplate, { args, data, item, index });\nif (!rendered || !/^https?:\\/\\//.test(rendered)) {\n  return { error: `invalid rendered URL for item ${index}` };\n}\nconst itemUrl = String(rendered);","handlingStrategy":"type-guard","validationCode":"const rendered = render(urlTemplate, { args, data, item, index });\nif (!/^https?:\\/\\//.test(String(rendered))) throw new Error(`bad item URL at index ${index}`);","typeGuard":"function isItemFetchFailure<T>(r: T | { error: string }): r is { error: string } {\n  return typeof r === 'object' && r !== null && 'error' in r;\n}","tryCatchPattern":"try {\n  return await fetchSingle(null, itemUrl, ...);\n} catch (error) {\n  const message = getErrorMessage(error);\n  if (message.includes('timeout')) await sleep(500); // backoff before caller retry\n  return { error: message }; // keep the batch alive\n}","preventionTips":["Check required item fields exist before template rendering.","URI-encode interpolated path segments.","Cap concurrency to avoid 429 rate limiting.","Prefer explicit timeouts in fetchSingle over defaults."],"tags":["network","fetch","concurrency"],"backgroundTag":"fetch-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}