santifer/career-ops · error · Error

vc-portfolios: YC API fetch failed

Error message

vc-portfolios: YC API fetch failed — ${err.message}

What it means

fetchYCCompanies paginates the YC companies API (api.ycombinator.com/v0.1/companies). If the very first page (page === 1) fetch fails — whether from fetchWithTimeout throwing an HTTP error or res.json() failing to parse — the function aborts with 'vc-portfolios: YC API fetch failed — <cause>'. Failures on pages after the first are tolerated and return partial data, so this error specifically means the seed builder could not retrieve ANY YC data.

Solutions

  1. Check the inner err.message for the root cause (HTTP status vs JSON parse error) and test the URL directly with curl
  2. Wait and retry if rate-limited (429) or if the YC API is having an incident
  3. Verify network access from the environment running the seed script (CI containers often lack egress)
  4. If the v0.1 API is deprecated, update the script to the current YC API version or a YC company dataset export

Example fix

// before
node seeds/vc-portfolios.mjs
// Error: vc-portfolios: YC API fetch failed — HTTP 429: Too Many Requests
// after: add retry/backoff around the first page
curl -s -o /dev/null -w '%{http_code}' 'https://api.ycombinator.com/v0.1/companies?page=1&per_page=1000'
// if 429, wait (e.g. sleep 60) or reduce request frequency, then rerun
Defensive patterns

Strategy: retry

Validate before calling

const reachable = await fetch('https://api.ycombinator.com/v0.1/companies?page=1&per_page=1')
  .then(r => r.ok).catch(() => false);
if (!reachable) { /* abort or defer the YC seed step */ }

Try / catch

try {
  const companies = await fetchYCCompanies({ timeoutMs });
} catch (err) {
  console.error(`YC seed unavailable: ${err.message}`);
  // retry later or continue with other seed sources
}

Prevention

When it happens

Trigger: Calling fetchYCCompanies when page 1 of https://api.ycombinator.com/v0.1/companies returns a non-2xx status (rate limited, API down) or returns a body that is not valid JSON (HTML error page, empty body), causing fetchWithTimeout or res.json() to throw.

Common situations: YC API outage or deprecation of the v0.1 endpoint; rate limiting after repeated seed runs; a captive portal / proxy returning an HTML page so res.json() throws SyntaxError; DNS/network failure in a CI job without internet access.

Related errors


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

Appendix: source

Thrown at seeds/vc-portfolios.mjs:347

 */
export async function fetchYCCompanies({ timeoutMs = DEFAULT_TIMEOUT_MS, maxPages = YC_MAX_PAGES } = {}) {
  /** @type {SeedCompany[]} */
  const all = [];
  const seen = new Set();

  // YC_MAX_PAGES is a hard ceiling: clamp here so an explicit maxPages (or a
  // stray Infinity) can never spin the walk past the runaway guard.
  const limit = Math.min(maxPages, YC_MAX_PAGES);

  let page = 1;
  for (let fetched = 0; fetched < limit; fetched++) {
    const url = `https://api.ycombinator.com/v0.1/companies?page=${page}&per_page=1000`;
    let payload;
    try {
      const res = await fetchWithTimeout(url, { timeoutMs });
      payload = await res.json();
    } catch (err) {
      if (page === 1) throw new Error(`vc-portfolios: YC API fetch failed — ${err.message}`);
      break; // Partial data is fine after page 1.
    }

    const entries = parseYCPayload(payload);
    if (entries.length === 0) break; // No more companies.

    for (const e of entries) {
      if (!seen.has(e.slug)) {
        seen.add(e.slug);
        all.push(e);
      }
    }

    // The API caps page size server-side (~30/page; per_page is ignored) and
    // reports totalPages — follow its signal instead of guessing from batch size.
    const raw = /** @type {any} */ (payload);
    if (Number.isInteger(raw?.totalPages) && raw.totalPages > 0) {
      if (page >= raw.totalPages) break;

View on GitHub (pinned to 1696bec4d0)