{"record":{"id":"615824e6b9920c32","repo":"santifer/career-ops","slug":"getro-entry-name-needs-a-numeric-getro-collec","errorCode":null,"errorMessage":"getro: ${entry.name} needs a numeric 'getro_collection' in portals.yml","messagePattern":"getro: (.+?) needs a numeric 'getro_collection' in portals\\.yml","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/getro.mjs","lineNumber":75,"sourceCode":"  const id = entry.getro_collection;\n  if (id == null) return null;\n  const s = String(id).trim();\n  if (!/^\\d+$/.test(s)) return null;\n  return s;\n}\n\n/** @type {Provider} */\nexport default {\n  id: 'getro',\n\n  detect(entry) {\n    const id = resolveCollection(entry);\n    return id ? { url: `${API_BASE}/${id}/search/jobs` } : null;\n  },\n\n  async fetch(entry, ctx) {\n    const id = resolveCollection(entry);\n    if (!id) throw new Error(`getro: ${entry.name} needs a numeric 'getro_collection' in portals.yml`);\n    const apiUrl = `${API_BASE}/${id}/search/jobs`;\n    const maxPages = Number.isInteger(entry.getro_max_pages) && entry.getro_max_pages > 0\n      ? Math.min(entry.getro_max_pages, HARD_MAX_PAGES) : DEFAULT_MAX_PAGES;\n    const maxAgeDays = Number.isFinite(entry.getro_max_age_days) && entry.getro_max_age_days >= 0\n      ? entry.getro_max_age_days : DEFAULT_MAX_AGE_DAYS;\n    const cutoffMs = maxAgeDays > 0 ? Date.now() - maxAgeDays * 86_400_000 : 0;\n\n    const out = [];\n    let total = Infinity;\n    for (let page = 0; page < maxPages && page * HITS_PER_PAGE < total; page++) {\n      const json = await ctx.fetchJson(apiUrl, {\n        method: 'POST',\n        // redirect:'error' — apiUrl is pinned to api.getro.com (https), so a 3xx\n        // to a private/metadata IP must not be followed (matches every provider).\n        redirect: 'error',\n        headers: { 'content-type': 'application/json', accept: 'application/json' },\n        body: JSON.stringify({ hitsPerPage: HITS_PER_PAGE, page }),\n      });","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/getro.mjs#L57-L93","documentation":"getro.mjs throws this at the top of fetch() when resolveCollection(entry) returns null. resolveCollection requires entry.getro_collection to be present and, after String().trim(), to match /^\\d+$/ — a pure-numeric collection id. Getro's API is keyed by a numeric collection_id (the network.id embedded in the board page), so without a valid one the provider cannot build the request URL.","triggerScenarios":"A portals.yml entry has provider: getro but no getro_collection; getro_collection is a non-numeric string (e.g. a board slug like 'b2venture'); getro_collection is null/undefined; the value is a quoted number that YAML parsed as a string with whitespace; the field was typo'd (e.g. collection_id, getro_id).","commonSituations":"Operator copies a Getro careers_url but forgets the numeric collection id; operator uses the board's textual slug instead of the numeric network.id; a field-name typo means the real value sits under an unrecognized key; YAML formatting made the number a string that still fails the regex after an accidental non-digit character.","solutions":["Open the Getro board page, view its __NEXT_DATA__ (or network tab), and copy the numeric network.id / collection_id.","Set getro_collection to that integer in portals.yml (e.g. getro_collection: 4283).","Double-check the field name is exactly getro_collection (not collection_id or getro_id).","Ensure the value is a bare integer or a numeric string with no spaces/quotes."],"exampleFix":"# before\n- name: b2venture (portfolio)\n  provider: getro\n  careers_url: https://jobs.b2venture.vc   # no numeric id -> throws\n\n# after\n- name: b2venture (portfolio)\n  provider: getro\n  getro_collection: 4283\n  careers_url: https://jobs.b2venture.vc","handlingStrategy":"validation","validationCode":"// Pre-flight check for a portals.yml entry before invoking the getro provider.\nfunction getroEntryIsValid(entry) {\n  if (entry.provider !== 'getro') return true;\n  const id = entry.getro_collection;\n  if (id == null) return false;\n  return /^\\d+$/.test(String(id).trim());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always source getro_collection from the board page's __NEXT_DATA__ network.id, not the slug.","Add a portals.yml lint pass that runs getroEntryIsValid on every provider: getro entry.","Use the documented field name exactly (getro_collection)."],"tags":["config","portals-yml","getro","provider-config","validation"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}