jackwener/OpenCLI · error

HTTP ${resp.status} ${resp.statusText} from ${urls[i]}

Error message

HTTP ${resp.status} ${resp.statusText} from ${urls[i]}

What it means

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().

Source

Thrown at src/pipeline/steps/fetch.ts:70

  const urlsJs = JSON.stringify(urls);
  const methodJs = JSON.stringify(method);
  return (await page.evaluate(`
    async () => {
      const urls = ${urlsJs};
      const method = ${methodJs};
      const headers = ${headersJs};
      const concurrency = ${concurrency};

      const results = new Array(urls.length);
      let idx = 0;

      async function worker() {
        while (idx < urls.length) {
          const i = idx++;
          try {
            const resp = await fetch(urls[i], { method, headers, credentials: "include" });
            if (!resp.ok) {
              throw new Error('HTTP ' + resp.status + ' ' + resp.statusText + ' from ' + urls[i]);
            }
            results[i] = await resp.json();
          } catch (e) {
            results[i] = { error: e instanceof Error ? e.message : String(e) };
            // Note: getErrorMessage() is a Node.js utility — can't use it inside evaluate()
          }
        }
      }

      const workers = Array.from({ length: Math.min(concurrency, urls.length) }, () => worker());
      await Promise.all(workers);
      return results;
    }
  `)) as unknown[];
}

export async function stepFetch(page: IPage | null, params: unknown, data: unknown, args: Record<string, unknown>): Promise<unknown> {
  const paramObject = isRecord(params) ? params : {};

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Scan results for entries with an .error property and read the embedded status/URL to identify the failing endpoint
  2. Fix or remove the failing URL from the batch list
  3. Re-authenticate so the include-credentials cookie session is valid for 401/403
  4. Reduce worker concurrency or add retry/backoff for 429/5xx responses

Example fix

// before
const results = await fetchBatchInBrowser(urls, { method: 'GET', headers });
console.log(results[3].data); // undefined, results[3] = { error: 'HTTP 404 Not Found from ...' }
// after
const results = await fetchBatchInBrowser(urls, { method: 'GET', headers });
for (const r of results) if (r.error) console.warn('failed:', r.error);
Defensive patterns

Strategy: validation

Validate before calling

const bad = urls.filter(u => { try { new URL(u); return false; } catch { return true; } });
if (bad.length) throw new Error('invalid batch urls: ' + bad.join(', '));

Type guard

function hasBatchErrors(results: unknown[]): results is Array<{ error: string }> {
  return results.some(r => typeof r === 'object' && r !== null && 'error' in r);
}

Try / catch

// Errors are captured per-item in results, not thrown:
const results = await fetchBatchInBrowser(urls, { method, headers });
for (const [i, r] of results.entries()) {
  if (r && typeof r === 'object' && 'error' in r) {
    console.warn(`batch item ${i} failed: ${r.error}`);
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/6c44a42d799cae00. Report an issue: GitHub.