santifer/career-ops · error · Error
HTTP ${res.status}${snippet ? ': ' + snippet.replace(/\s+/g,
Error message
HTTP ${res.status}${snippet ? ': ' + snippet.replace(/\s+/g, ' ').trim() : ''} What it means
fetchWithTimeout() in seeds/vc-portfolios.mjs performs an HTTP fetch with an AbortController-based timeout. If the response status is not ok (not 2xx), it reads up to 200 chars of the body as a snippet and throws an Error of the form `HTTP <status>: <snippet>`. This surfaces upstream server errors (4xx/5xx) with actionable context.
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 9b17a8ac97)
Solutions
- Retry with backoff for transient 5xx/429 — the snippet tells you which.
- If 403/blocked, run from a residential IP or set a browser-like User-Agent (DEFAULT_USER_AGENT is already set).
- Check the upstream API status page / changelog for URL or auth changes.
- Reduce polling frequency to avoid 429 rate limiting.
Example fix
// before: single attempt
const res = await fetchWithTimeout(url);
// after: retry with backoff on transient errors
for (let attempt = 0; attempt < 3; attempt++) {
try { return await fetchWithTimeout(url, { timeoutMs }); }
catch (err) {
if (attempt === 2 || !/HTTP [45]/.test(err.message)) throw err;
}
} Defensive patterns
Strategy: retry
Validate before calling
function classifyHttpError(err) {
const m = err.message.match(/HTTP (\d{3})/);
return m ? Number(m[1]) : null;
}
const status = classifyHttpError(err);
const transient = status && (status >= 500 || status === 429); Try / catch
async function fetchWithRetry(url, opts, retries = 3) {
for (let i = 0; i < retries; i++) {
try { return await fetchWithTimeout(url, opts); }
catch (err) {
const m = err.message.match(/HTTP (\d{3})/);
const status = m ? Number(m[1]) : 0;
const transient = status >= 500 || status === 429;
if (!transient || i === retries - 1) throw err;
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
}
}
} Prevention
- Wrap fetchWithTimeout in a retry-with-backoff for transient (5xx, 429) errors.
- Respect Retry-After headers on 429/503 responses.
- Run VC-portfolio fetches from IPs that are not datacenter-blocked when possible.
When it happens
Trigger: The VC portfolio API (YC or a16z endpoint) returns a non-2xx status: 403 (blocked/rate-limited), 404 (endpoint moved), 429 (rate limit), 500/502/503 (server error), or a CDN block page.
Common situations: Running scans from a datacenter IP that triggers Cloudflare blocks; hitting rate limits from aggressive polling; the upstream API changed its URL or requires auth; transient 5xx during maintenance.
Related errors
- vc-portfolios: YC API fetch failed — ${err.message}
- vc-portfolios: a16z portfolio fetch failed — ${err.message}
- DNS resolution returned no addresses for ${hostname}
- Access denied: Egress guard blocked private target IP ${ip}
- [models] Failed to fetch free model list: ${reason}. Check t
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/b680b877415aeab1.
Report an issue: GitHub.