{"record":{"id":"69f1113260db9ae6","repo":"santifer/career-ops","slug":"arbeitsagentur-entry-entry-name-unnamed","errorCode":null,"errorMessage":"arbeitsagentur: entry \"${entry.name || '(unnamed)'}\" has no arbeitsagentur.keywords[]","messagePattern":"arbeitsagentur: entry \"(.+?)\" has no arbeitsagentur\\.keywords\\[\\]","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/arbeitsagentur.mjs","lineNumber":153,"sourceCode":"// candidate whose title makes no remote claim keeps its real city, which is the\n// fail-closed behaviour an unverifiable lookup had in v4: the `Deutschlandweit\n// (Homeoffice)` marker exempts a job from the commute location_filter, so\n// tagging on nv_true alone would smuggle every hybrid past it.\n\n/** @type {Provider} */\nexport default {\n  id: 'arbeitsagentur',\n\n  /**\n   * Fetches and normalizes postings from the Arbeitsagentur Jobsuche API.\n   * @param {{ name?: string, arbeitsagentur?: any }} entry\n   * @param {{ fetchJson: (url: string, opts?: object) => Promise<any> }} ctx\n   * @returns {Promise<Array<{title: string, url: string, company: string, location: string}>>}\n   */\n  async fetch(entry, ctx) {\n    const { keywords, wo, umkreis, days, size, remoteNationwide, remoteMatch, remoteMaxPages } = parseArbeitsagenturConfig(entry);\n    if (!keywords.length) {\n      throw new Error(`arbeitsagentur: entry \"${entry.name || '(unnamed)'}\" has no arbeitsagentur.keywords[]`);\n    }\n\n    /** @param {string} was @param {Record<string,string>} [extra] */\n    const fetchKeyword = async (was, extra = {}) => {\n      const params = new URLSearchParams({\n        was,\n        size: String(size),\n        page: '1',\n        angebotsart: '1', // 1 = ARBEIT (employment; excludes Ausbildung/Selbständigkeit)\n        veroeffentlichtseit: String(days),\n        ...extra,\n      });\n      // redirect:'error' prevents SSRF via server-side redirects.\n      const json = await ctx.fetchJson(`${API_URL}?${params.toString()}`, {\n        headers: { 'X-API-Key': API_KEY, accept: 'application/json' },\n        redirect: 'error',\n        timeoutMs: 12_000,\n      });","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/arbeitsagentur.mjs#L135-L171","documentation":"The Arbeitsagentur provider derives its search terms from `entry.arbeitsagentur.keywords[]` via `parseArbeitsagenturConfig`. If that array is empty/absent the provider has nothing to query and throws immediately, naming the entry (or '(unnamed)') so the offending config row is identifiable.","triggerScenarios":"`parseArbeitsagenturConfig(entry).keywords` is an empty array — i.e. `entry.arbeitsagentur` is missing, has no `keywords` key, or `keywords` is `[]`. Thrown at the top of `fetch` before any network call.","commonSituations":"A portals.yml entry enables the arbeitsagentur provider but omits the `keywords` list; a YAML indentation error puts keywords under the wrong key; or a templated entry was left with an empty array as a placeholder.","solutions":["Add an `arbeitsagentur.keywords` array to the entry, e.g. `keywords: ['Softwareentwickler', 'IT-Projektmanager']`.","Verify the YAML nesting: `keywords` must sit under `arbeitsagentur:`, not at the entry root.","Run a config linter/doctor check to catch empty keyword arrays before the scan starts."],"exampleFix":"# before\n- name: Bundesagentur für Arbeit\n  provider: arbeitsagentur\n  arbeitsagentur:\n    wo: Berlin\n\n# after\n- name: Bundesagentur für Arbeit\n  provider: arbeitsagentur\n  arbeitsagentur:\n    wo: Berlin\n    keywords:\n      - Softwareentwickler\n      - IT-Projektmanager","handlingStrategy":"validation","validationCode":"// Validate entry config before invoking the provider\nfunction validateArbeitsagenturEntry(entry) {\n  const kw = entry?.arbeitsagentur?.keywords;\n  if (!Array.isArray(kw) || kw.length === 0 || kw.some(k => typeof k !== 'string' || !k.trim())) {\n    throw new Error(`arbeitsagentur: entry \"${entry?.name || '(unnamed)'}\" needs a non-empty arbeitsagentur.keywords[] of strings`);\n  }\n}\nvalidateArbeitsagenturEntry(entry);","typeGuard":"/** @param {any} e */\nfunction hasArbeitsagenturKeywords(e) {\n  return e?.arbeitsagentur?.keywords instanceof Array && e.arbeitsagentur.keywords.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Add a config schema check (e.g. ajv/zod) over portals.yml entries before the scan.","Reject empty keyword arrays at validation time, not at fetch time.","Run a `doctor`/lint pass that flags arbeitsagentur entries missing keywords."],"tags":["arbeitsagentur","config","validation","yaml"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}