{"record":{"id":"86a8fbb17a13e3ba","repo":"santifer/career-ops","slug":"could-not-fetch-job-page-e-message","errorCode":null,"errorMessage":"Could not fetch job page: ${e.message}","messagePattern":"Could not fetch job page: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"openrouter-runner.mjs","lineNumber":435,"sourceCode":"      });\n      return text.slice(0, 16_000);\n    } catch (e) {\n      console.warn(`[fetch] Playwright error: ${e.message} — falling back to plain fetch.`);\n    } finally {\n      if (browser) await browser.close().catch(() => {});\n    }\n  }\n\n  // Plain HTTP fallback\n  try {\n    const r = await fetch(url, {\n      headers: { 'User-Agent': DEFAULT_USER_AGENT }\n    });\n    if (!r.ok) throw new Error(`HTTP ${r.status} ${r.statusText}`);\n    const html = await r.text();\n    return html.replace(/<[^>]+>/g, ' ').replace(/\\s+/g, ' ').trim().slice(0, 16_000);\n  } catch (e) {\n    throw new Error(`Could not fetch job page: ${e.message}`);\n  }\n}\n\n// ---------------------------------------------------------------------------\n// portals.yml parser — reads the canonical schema with js-yaml (same library and\n// field names as scan.mjs: `title_filter.positive/negative` + `tracked_companies`),\n// so it never drifts from the main scanner. The runner's no-CLI scan path covers\n// companies that expose a direct JSON `api:`; careers_url-only / Playwright /\n// search-query companies are handled by the full /career-ops scan pipeline.\n// `rawOverride` lets tests feed YAML text directly (see test-all.mjs drift guard).\n// ---------------------------------------------------------------------------\nfunction normKeywords(v) {\n  if (!Array.isArray(v)) return [];\n  return v.map(x => String(x ?? '').toLowerCase().trim()).filter(Boolean);\n}\n\nexport function parsePortals(rawOverride) {\n  const raw = rawOverride ?? readFile('portals.yml');","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/openrouter-runner.mjs#L417-L453","documentation":"Outer wrapper thrown by fetchJobPage()'s plain-fetch fallback catch block; it wraps any underlying failure (the HTTP-status error from the inner throw, a DNS failure, a connection reset, or an abort) into a single 'Could not fetch job page' error with the original message preserved. It is the user-facing fetch failure surface.","triggerScenarios":"Any fetch failure in the plain-fetch fallback path: non-2xx (wrapped from error 82), ENOTFOUND/ECONNREFUSED/ECONNRESET, TLS certificate error, or AbortError from a timeout. It fires after Playwright either is unavailable or also failed.","commonSituations":"No network/DNS for the host; corporate firewall blocks the ATS; expired TLS cert; the host is up but resets the connection due to bot detection; Playwright missing AND remote is down.","solutions":["Read the inner message after the colon — it carries the real cause (HTTP code, ENOTFOUND, ECONNRESET, etc.).","If the inner cause is a connection/DNS error, confirm connectivity to the host from a browser or curl.","If the inner cause is HTTP status, follow the fix for error 82.","Install playwright so the browser path is attempted before this fallback.","Mark the pipeline entry dead if the page is genuinely unreachable."],"exampleFix":"// before\nthrow new Error(`Could not fetch job page: ${e.message}`);\n// after: surface the status code so callers can branch\nconst cause = e.status ? ` (HTTP ${e.status})` : `: ${e.message}`;\nthrow new Error(`Could not fetch job page${cause}`);","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  const html = await fetchJobPage(url);\n} catch (e) {\n  // e.message starts with 'Could not fetch job page:'; the cause is after the colon\n  console.error('fetch failed:', e.message);\n  // decide: retry (transient), drop (404), or escalate\n}","preventionTips":["Treat the text after 'Could not fetch job page:' as the real cause and branch on it.","Install playwright so the browser fallback is attempted before plain fetch.","Don't surface this error to end users verbatim — translate it into an actionable status."],"tags":["network","fetch","error-wrapping","openrouter-runner"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}