santifer/career-ops · error · Error

HTTP

Error message

HTTP ${res.status}${snippet ? ': ' + snippet.replace(/\s+/g, ' ').trim() : ''}

What it means

fetchWithTimeout in seeds/vc-portfolios.mjs wraps node fetch and throws whenever the HTTP response status is not ok (res.ok false, i.e. status outside 200-299). To aid debugging it appends up to 200 characters of the response body (whitespace-collapsed) as a snippet. This is the library's generic non-2xx response error for all remote seed-source fetches.

Solutions

  1. Read the status code and body snippet in the message to identify the cause (403/429 → rate limiting or bot protection; 404 → wrong URL; 5xx → retry later)
  2. Retry with exponential backoff after a delay, especially for 429/5xx responses
  3. Send a realistic User-Agent (the library already sends DEFAULT_USER_AGENT; some hosts require a browser-like UA or an API key)
  4. Verify the endpoint URL is still valid (APIs change; check the provider's docs) and update A16Z_PORTFOLIO_URL / the YC API URL if deprecated

Example fix

// before
const res = await fetchWithTimeout(url, { timeoutMs });
// after: retry with backoff on transient statuses
let res;
for (let attempt = 0; attempt < 3; attempt++) {
  try { res = await fetchWithTimeout(url, { timeoutMs }); break; }
  catch (e) {
    if (!/HTTP (429|5\d\d)/.test(e.message) || attempt === 2) throw e;
    await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// No pre-call validation possible for remote status; check reachability/health first:
const healthy = await fetch(url, { method: 'HEAD' }).then(r => r.ok).catch(() => false);
if (!healthy) console.warn('endpoint unhealthy — expect HTTP non-2xx');

Try / catch

try {
  const res = await fetchWithTimeout(url, { timeoutMs });
  // use res
} catch (err) {
  const m = /HTTP (\d{3})/.exec(err.message);
  const status = m ? Number(m[1]) : 0;
  if (status === 429 || status >= 500) { /* back off and retry */ }
  else if (status === 403 || status === 404) { /* fix UA/URL — retrying won't help */ }
  else throw err;
}

Prevention

When it happens

Trigger: Any fetch call made through fetchWithTimeout that returns 404 (page moved), 403 (bot blocked / rate limited), 429 (too many requests), 5xx (server error), or a redirect-to-error from the remote host. The error is thrown inside the try block and then re-wrapped by callers like fetchYCCompanies/fetchA16zCompanies.

Common situations: Hitting api.ycombinator.com while rate-limited or during an outage; a company's portfolio page returning 403 because of Cloudflare bot protection; a typo'd or deprecated endpoint URL; corporate proxy returning an error page.

Related errors


AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/b680b877415aeab1. Report an issue: GitHub.

Appendix: source

Thrown at seeds/vc-portfolios.mjs:81

/**
 * Minimal fetch wrapper with timeout + user-agent header.
 *
 * @param {string} url
 * @param {{ timeoutMs?: number }} [opts]
 * @returns {Promise<Response>}
 */
async function fetchWithTimeout(url, { timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const res = await fetch(url, {
      headers: { 'user-agent': DEFAULT_USER_AGENT },
      signal: controller.signal,
    });
    if (!res.ok) {
      const snippet = await res.text().catch(() => '').then(t => t.slice(0, 200));
      throw new Error(`HTTP ${res.status}${snippet ? ': ' + snippet.replace(/\s+/g, ' ').trim() : ''}`);
    }
    return res;
  } finally {
    clearTimeout(timer);
  }
}

// ── Shared types (JSDoc only — no runtime cost) ──────────────────────

/**
 * A single VC-portfolio company entry — the output unit of both seed fetchers.
 *
 * @typedef {object} SeedCompany
 * @property {string}   name            Display name, e.g. "Stripe".
 * @property {string}   slug            URL-safe slug, validated against SLUG_RE.
 * @property {string}   url             Company website URL.
 * @property {string}   [ats]           ATS platform if detectable: 'greenhouse' | 'lever' | 'ashby'.
 * @property {string}   [ats_id]        ATS board/org slug for URL construction.

View on GitHub (pinned to 1696bec4d0)