santifer/career-ops · error · Error

getro: ${label} — could not resolve collection_id from ${car

Error message

getro: ${label} — could not resolve collection_id from ${careersUrl.href} (no network.id found in __NEXT_DATA__; page structure may have changed — set getro_collection: N on this entry as a fallback)

What it means

The getro provider auto-discovers a collection_id by fetching the company's careers page (with redirect:'error') and scraping network.id out of the embedded __NEXT_DATA__ JSON. If extractCollectionId finds no id in the HTML, resolveCollectionId throws this error naming the entry label and the careers URL. It means the page no longer exposes the id where expected — or the page wasn't reached as anticipated.

Source

Thrown at providers/getro.mjs:157

  return null;
}

/** Override wins; otherwise fetch careers_url and parse __NEXT_DATA__. */
async function resolveCollectionId(entry, ctx, careersUrl) {
  const override = resolveCollectionOverride(entry);
  if (override) return override;

  const label = entry?.name || careersUrl.href;
  // Retried like every page fetch below — this single request runs BEFORE
  // pagination even starts, so without a retry a transient blip here (DNS/TLS/
  // connection reset) fails the whole board before a single page is fetched.
  const html = await fetchTextWithRetry(ctx, careersUrl.href, {
    redirect: 'error',
    headers: { accept: 'text/html', 'user-agent': BROWSER_LIKE_USER_AGENT },
  });
  const id = extractCollectionId(html);
  if (!id) {
    throw new Error(
      `getro: ${label} — could not resolve collection_id from ${careersUrl.href} (no network.id found in ` +
      `__NEXT_DATA__; page structure may have changed — set getro_collection: N on this entry as a fallback)`,
    );
  }
  return id;
}

/**
 * `{min, max, currency}` shape scan.mjs's salary_filter consumes, or null
 * when there's no usable figure. A non-year compensation_period
 * (hourly/monthly/etc.) is treated as "no usable annual figure".
 */
function getroSalary(job) {
  const period = typeof job?.compensation_period === 'string' ? job.compensation_period.trim().toLowerCase() : '';
  if (period && period !== 'year') return null;
  const minCents = Number(job?.compensation_amount_min_cents);
  const maxCents = Number(job?.compensation_amount_max_cents);
  const min = Number.isFinite(minCents) && minCents > 0 ? minCents / 100 : null;

View on GitHub (pinned to 1696bec4d0)

Solutions

  1. Set the documented fallback on the entry: `getro_collection: N` with the collection id taken from the careers page's __NEXT_DATA__ (view-source in a browser) or an existing API call.
  2. Open careersUrl.href in a browser and confirm it still renders a Getro board; update careers_url if the company moved.
  3. Inspect the page source to see if __NEXT_DATA__ moved to a new script/structure and update extractCollectionId.
  4. Check whether a bot challenge is returned (curl the URL); reduce request frequency or adjust headers if so.

Example fix

// before (portals entry)
{ name: 'Acme', provider: 'getro', careers_url: 'https://acme.getro.com/careers' }
// after
{ name: 'Acme', provider: 'getro', careers_url: 'https://acme.getro.com/careers', getro_collection: 12345 }
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-check the page before auto-discovery:
const html = await fetch(careersUrl).then(r => r.text());
const hasId = /"network"\s*:\s*\{[^}]*"id"\s*:\s*\d+/.test(html);
if (!hasId) console.warn(`getro: no network.id on ${careersUrl} — set getro_collection manually`);

Type guard

function hasResolvedCollection(entry) {
  return Number.isInteger(entry.getro_collection) && entry.getro_collection > 0;
}

Try / catch

try {
  const id = await resolveCollectionId(entry, ctx, careersUrl);
} catch (e) {
  if (e.message.includes('could not resolve collection_id')) {
    console.warn(`${entry.name}: ${e.message}`);
    if (entry.getro_collection) return useOverride(entry.getro_collection);
    return null; // skip entry, keep scan running
  }
  throw e;
}

Prevention

When it happens

Trigger: fetchTextWithRetry returns HTML without a network.id inside __NEXT_DATA__ — e.g. a client-side-only rendered page, a bot-challenge/Cloudflare interstitial, a redesigned careers page, or the company moved off Getro — for a getro entry with no getro_collection override.

Common situations: Getro changed its Next.js data structure; the company's careers URL now redirects (blocked by redirect:'error'); scraping is blocked by a WAF returning challenge HTML; the entry's careers_url points to the wrong page.

Related errors


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