santifer/career-ops · error · Error

rheinmetall: cannot resolve vacancies URL for

Error message

rheinmetall: cannot resolve vacancies URL for ${entry.name}

What it means

The rheinmetall provider derives the paginated vacancies list URL from the provider entry via resolveListUrl. If the entry (or its detected candidate) yields no resolvable list URL, fetch throws this error naming the entry. It prevents proceeding with an undefined request target.

Solutions

  1. Add/fix the api URL field on the provider entry so resolveListUrl returns a valid list URL.
  2. Verify the vacancies endpoint still exists (open it in a browser); update the entry if the ATS migrated.
  3. Log the entry object before fetch to see which key resolveListUrl expects and align the config.

Example fix

// before
{ name: 'rheinmetall' }
// after
{ name: 'rheinmetall', api: 'https://jobs.rheinmetall.com/api/vacancies' }
Defensive patterns

Strategy: validation

Validate before calling

function entryHasListUrl(entry) { return entry && typeof entry.api === 'string' && entry.api.startsWith('https://'); }
if (!entryHasListUrl(entry)) console.warn(`Entry ${entry.name} lacks a resolvable vacancies URL`);

Type guard

const hasApi = (e) => typeof e?.api === 'string' && e.api.length > 0;

Try / catch

try { jobs = await provider.fetch(entry, ctx); } catch (e) { if (e.message.includes('cannot resolve vacancies URL')) { console.error(`Fix entry config for ${entry.name}: missing api URL`); return null; } throw e; }

Prevention

When it happens

Trigger: Calling fetch with an entry whose name yields no api URL — entry missing the api field, the field is not a string, or resolveListUrl({api:url}) returns null for the candidate value.

Common situations: A portals.yml entry for Rheinstoff/Rheinmetall missing the careers API URL key after a config edit, a typo'd key name, or an ATS migration that changed the vacancies endpoint so detection no longer resolves it.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/665b366c86594726. Report an issue: GitHub.

Appendix: source

Thrown at providers/rheinmetall.mjs:114

function resolveMaxPages(entry) {
  const v = entry?.max_pages;
  if (Number.isInteger(v) && v > 0) return Math.min(v, MAX_PAGES);
  return MAX_PAGES;
}

/** @type {Provider} */
export default {
  id: 'rheinmetall',

  detect(entry) {
    const url = entry.api || entry.careers_url || '';
    if (typeof url !== 'string') return null;
    return resolveListUrl({ api: url }) ? { url } : null;
  },

  async fetch(entry, ctx) {
    const listUrl = resolveListUrl(entry);
    if (!listUrl) throw new Error(`rheinmetall: cannot resolve vacancies URL for ${entry.name}`);
    const origin = new URL(listUrl).origin;

    const wait = (ms) => (ctx.sleep ? ctx.sleep(ms) : new Promise((r) => setTimeout(r, ms)));
    const maxPages = resolveMaxPages(entry);
    const jobs = [];
    const seen = new Set();

    for (let page = 1; page <= maxPages; page++) {
      if (page > 1) await wait(PAGE_DELAY_MS);
      const html = await ctx.fetchText(`${listUrl}?page=${page}`, {
        headers: { accept: 'text/html' },
      });
      const rows = parseVacancies(html, origin);
      if (rows.length === 0) {
        if (page === 1) console.warn(`rheinmetall: page 1 returned no vacancy cards for ${entry.name} — markup may have changed`);
        break; // past the last page
      }

View on GitHub (pinned to aac998c7ed)