{"record":{"id":"c326befe5123a95b","repo":"santifer/career-ops","slug":"consider-entry-name-needs-a-consider-board-i","errorCode":null,"errorMessage":"consider: ${entry.name} needs a 'consider_board' id in portals.yml","messagePattern":"consider: (.+?) needs a 'consider_board' id in portals\\.yml","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/consider.mjs","lineNumber":151,"sourceCode":"  if (Array.isArray(job.normalizedLocations) && job.normalizedLocations.length) {\n    return job.normalizedLocations.map(l => l?.label || l?.value).filter(Boolean).join(', ');\n  }\n  return job.remote ? 'Remote' : '';\n}\n\n/** @type {Provider} */\nexport default {\n  id: 'consider',\n\n  detect(entry) {\n    const origin = resolveOrigin(entry);\n    return entry.consider_board && origin ? { url: origin + ENDPOINT_PATH } : null;\n  },\n\n  async fetch(entry, ctx) {\n    const origin = resolveOrigin(entry);\n    if (!origin) throw new Error(`consider: ${entry.name} needs an https careers_url on a public host`);\n    if (!entry.consider_board) throw new Error(`consider: ${entry.name} needs a 'consider_board' id in portals.yml`);\n    const size = Number.isInteger(entry.consider_size) && entry.consider_size > 0 ? entry.consider_size : DEFAULT_SIZE;\n\n    // Perform the CSRF handshake before the POST. ctx._acquireHandshake is a\n    // test seam: set it to a stub in unit tests so no real network call is made.\n    const { cookie, csrfToken } = await (\n      typeof ctx._acquireHandshake === 'function'\n        ? ctx._acquireHandshake(origin)\n        : acquireCsrfHandshake(origin)\n    );\n\n    const csrfHeaders = {};\n    if (cookie) csrfHeaders.cookie = cookie;\n    if (csrfToken) csrfHeaders['x-csrf-token'] = csrfToken;\n\n    const json = await ctx.fetchJson(origin + ENDPOINT_PATH, {\n      method: 'POST',\n      // redirect:'error' so a 3xx from the (config-driven) board host can't be\n      // followed to a private/metadata IP — the host guard above pins the first hop.","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/providers/consider.mjs#L133-L169","documentation":"The Consider board id is not derivable from the hostname (e.g. Founderful's board id is 'wingman'), so the entry must declare it explicitly via `consider_board` in portals.yml. fetch throws this when the origin resolved fine but entry.consider_board is missing/empty, since the search POST body needs board.id.","triggerScenarios":"A consider entry with a valid public https careers_url but no consider_board field, an empty string, or a wrong-typed value (e.g. consider_size set instead). The check `if (!entry.consider_board)` fires right after the origin check passes.","commonSituations":"Assuming the board id equals the subdomain (it usually doesn't); copying an entry template from another provider (greenhouse/ashby style) that has no consider_board key; renaming the field by mistake (board, board_id).","solutions":["Add `consider_board: <board-id>` to the entry, e.g. consider_board: wingman for Founderful.","Find the board id from a headless/network capture of the board's POST to /api-boards/search-jobs (the body's board.id field).","Fix field-name typos — the provider reads exactly entry.consider_board.","Verify the id is a non-empty string (numbers coerce via String() in the body, but empty/undefined throws)."],"exampleFix":"# before\n- name: Founderful (portfolio)\n  provider: consider\n  careers_url: https://jobs.founderful.com/jobs\n# after\n- name: Founderful (portfolio)\n  provider: consider\n  consider_board: wingman\n  careers_url: https://jobs.founderful.com/jobs","handlingStrategy":"validation","validationCode":"function hasConsiderBoard(entry) {\n  return typeof entry.consider_board === 'string' && entry.consider_board.length > 0;\n}\nif (entry.provider === 'consider' && !hasConsiderBoard(entry)) {\n  throw new Error(`${entry.name}: consider_board is required`);\n}","typeGuard":"function hasConsiderBoardId(e) {\n  return e != null && typeof e.consider_board === 'string' && e.consider_board.trim().length > 0;\n}","tryCatchPattern":"try {\n  await scanEntry(entry);\n} catch (e) {\n  if (e.message.includes(\"needs a 'consider_board' id\")) {\n    console.error(`${entry.name}: add consider_board: <board-id> (not derivable from the hostname)`);\n  } else throw e;\n}","preventionTips":["Always set consider_board explicitly — the board id (e.g. 'wingman') is not the subdomain.","Discover the id once via the board's /api-boards/search-jobs POST body and record it in portals.yml.","Check for provider-template leftovers when adding consider entries from a greenhouse/ashby template.","Add a schema check per provider: consider entries require careers_url + consider_board."],"tags":["config","missing-config","provider"],"backgroundTag":"missing-config-field","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}