santifer/career-ops · warning

portals.yml: " " is enabled but no provider claims —…

Error message

portals.yml: "${e.name}" is enabled but no provider claims ${e.careers_url || 'its careers_url'} — scan.mjs skips it on every run without naming it (run node audit-portals.mjs)

What it means

verify-pipeline.mjs Check 15 warns about portals.yml 'silent' entries: enabled, named, but no provider module in providers/ claims the entry's careers_url. scan.mjs skips such entries on every run without any message, so the board reads as covered while contributing zero postings. The audit companion is audit-portals.mjs.

Solutions

  1. Run `node audit-portals.mjs` to see what the URL actually serves and whether any provider claims it
  2. Replace careers_url with the company's real ATS board URL (e.g. boards.greenhouse.io/<tenant>)
  3. If no ATS board exists, either disable the entry (enabled: false) or switch it to a websearch-handoff entry
  4. Re-run verify-pipeline.mjs and confirm the entry now appears in the 'resolve to a provider' count

Example fix

# before (custom careers page, never scanned)
- name: Acme
  careers_url: https://acme.com/careers
# after (real ATS board)
- name: Acme
  careers_url: https://boards.greenhouse.io/acme
  provider: greenhouse
Defensive patterns

Strategy: validation

Validate before calling

const KNOWN = /boards\.greenhouse\.io|jobs\.lever\.co|jobs\.ashbyhq\.com|.*\.wd\d*\.myworkdayjobs\.com|careers-.*\.icims\.com/;
for (const e of entries.filter(e => e && e.enabled !== false)) {
  if (!KNOWN.test(e.careers_url || '')) console.warn(`no provider claims ${e.name}: ${e.careers_url}`);
}

Type guard

const isProviderBacked = (e) => e && KNOWN_ATS_HOSTS.some(h => (e.careers_url || '').includes(h));

Prevention

When it happens

Trigger: Running `node verify-pipeline.mjs` when an enabled portals.yml entry's careers_url doesn't match any known ATS URL pattern (Greenhouse/Lever/Ashby/Workday/iCIMS) or lacks a recognized provider field — typically after adding a company whose careers page is a custom portal.

Common situations: Adding a company with a generic careers site URL instead of its ATS board URL; ATS migration changed the board host so the pattern no longer matches; typos in careers_url.

Related errors


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

Appendix: source

Thrown at verify-pipeline.mjs:570

    await mergeProviderPlugins(providers, { root: CODE_ROOT });
    const { silent, handoff, unknownProvider } = findUnclaimedEntries(entries, providers);

    // findUnclaimedEntries silently skips an entry with no (or blank) `name` —
    // it can't report what it can't label. Without this, that entry vanishes
    // from silent/handoff/unknownProvider entirely, and the enabled count
    // below (which doesn't share the same eligibility rule) would still
    // include it — so a malformed entry never gets a provider check AND the
    // "All N entries resolve" success line claims it as resolved anyway.
    const malformed = entries.filter(e => e && e.enabled !== false && (typeof e.name !== 'string' || !e.name.trim()));
    for (const e of malformed) {
      warn(`portals.yml: an enabled entry has no name (careers_url: ${e.careers_url || 'none'}) — it cannot be provider-checked; give it a name`);
    }

    for (const e of unknownProvider) {
      error(`portals.yml: "${e.name}" sets an unknown provider — ${e.error}. The entry never scans (see providers/ for valid ids)`);
    }
    for (const e of silent) {
      warn(`portals.yml: "${e.name}" is enabled but no provider claims ${e.careers_url || 'its careers_url'} — scan.mjs skips it on every run without naming it (run node audit-portals.mjs)`);
    }
    if (silent.length === 0 && unknownProvider.length === 0 && malformed.length === 0) {
      const enabled = entries.filter(e => e && e.enabled !== false).length;
      ok(handoff.length > 0
        ? `All ${enabled - handoff.length} scannable portals.yml entries resolve to a provider (${handoff.length} on websearch handoff)`
        : `All ${enabled} enabled portals.yml entries resolve to a provider`);
    }
  } catch (err) {
    warn(`Portal coverage check could not run: ${err.message}`);
  }
}

// --- Check 16: invisible control bytes already in tracker cells (#3892) ---
// cell() in tracker-utils.mjs strips these on the way in, which stops new ones
// entering but can do nothing about the ones already written. This is the only
// place such a byte is visible at all: it shifts or truncates the positional
// `split('|')` parse, so a row silently reads as a different row or drops out
// of a count entirely, while every renderer of the table — markdown, GitHub,

View on GitHub (pinned to e7abd431fc)