santifer/career-ops · error · Error

jobbankca: entry "${entry?.name || '(unnamed)'}" has no jobb

Error message

jobbankca: entry "${entry?.name || '(unnamed)'}" has no jobbankca.keywords[] and no config/profile.yml target_roles to fall back to

What it means

fetch() requires a keyword list to build jobbankca search requests. It takes the entry's own jobbankca.keywords[], else falls back to target_roles in config/profile.yml; if both are empty it throws. The library refuses to issue a query-less fetch because Job Bank searches are keyword-driven.

Source

Thrown at providers/jobbankca.mjs:248

}

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

  detect(entry) {
    return entry?.provider === 'jobbankca' ? { url: FEED_URL } : null;
  },

  async fetch(entry, ctx) {
    let { keywords } = parseJobBankConfig(entry);
    // Fall back to config/profile.yml's target_roles when this entry has no
    // jobbankca.keywords[] of its own — same convention vdab.mjs uses, so a
    // user who already onboarded with target roles doesn't have to duplicate
    // them into every keyword-required provider's config by hand.
    if (!keywords.length) keywords = resolveProfileKeywords();
    if (!keywords.length) {
      throw new Error(`jobbankca: entry "${entry?.name || '(unnamed)'}" has no jobbankca.keywords[] and no config/profile.yml target_roles to fall back to`);
    }

    const entryMaxPages = Number.isInteger(entry?.max_pages) && entry.max_pages > 0
      ? Math.min(entry.max_pages, MAX_PAGES_CAP)
      : DEFAULT_MAX_PAGES;
    const maxPages = Math.min(
      entryMaxPages,
      Number.isInteger(ctx?.maxPages) && ctx.maxPages > 0 ? ctx.maxPages : Infinity,
    );

    /** @type {Map<string, {title: string, url: string, company: string, location: string, postedAt?: number}>} */
    const byUrl = new Map();
    const errors = [];
    let succeeded = 0;

    for (const keyword of keywords) {
      let keywordFailed = false;
      for (let page = 1; page <= maxPages; page++) {

View on GitHub (pinned to 1696bec4d0)

Solutions

  1. Add jobbankca.keywords: [...] to the jobbankca entry in portals.yml
  2. Or set target_roles in config/profile.yml so the provider can fall back to it
  3. Verify YAML key spelling/nesting: keywords must be exactly jobbankca.keywords[] under the entry
  4. Check that your config loader actually resolves profile.yml from the expected data root (CAREER_OPS_DATA_DIR)

Example fix

// before (portals.yml)
- name: Job Bank CA
  provider: jobbankca
// after
- name: Job Bank CA
  provider: jobbankca
  jobbankca:
    keywords: ["data engineer", "backend developer"]
Defensive patterns

Strategy: validation

Validate before calling

const entryKeywords = entry?.jobbankca?.keywords ?? [];
const profileRoles = profile?.target_roles ?? [];
if (!entryKeywords.length && !profileRoles.length) {
  throw new Error(`entry ${entry?.name}: no keywords and no target_roles fallback configured`);
}

Type guard

function hasKeywords(entry) {
  return Array.isArray(entry?.jobbankca?.keywords) && entry.jobbankca.keywords.length > 0;
}

Try / catch

try {
  await jobbankca.fetch(entry, ctx);
} catch (e) {
  if (e.message.includes('no jobbankca.keywords')) {
    console.error(`Fix portals.yml entry "${entry?.name}": add jobbankca.keywords or set target_roles in config/profile.yml`);
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Running a scan/pipeline with a portals.yml jobbankca entry lacking jobbankca.keywords[], while config/profile.yml has no target_roles (or an empty list) — usually a freshly onboarded or template profile.

Common situations: New user skipped personalization of config/profile.yml; entry name present but keywords key misspelled (e.g. keyword: vs keywords:); target_roles defined under the wrong YAML nesting level; entry?.name undefined shows '(unnamed)' indicating a malformed entry object.

Related errors


AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/662cfe7129d0571a. Report an issue: GitHub.