{"record":{"id":"43451d60ab2fad21","repo":"santifer/career-ops","slug":"getonbrd-categories-is-empty-omit-it-to-use-t","errorCode":null,"errorMessage":"getonbrd: `categories` is empty — omit it to use the \"programming\" default","messagePattern":"getonbrd: `categories` is empty — omit it to use the \"programming\" default","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/getonbrd.mjs","lineNumber":65,"sourceCode":" * @returns {string[]}\n */\nexport function resolveCategories(entry) {\n  const raw = entry?.categories !== undefined ? entry.categories : entry?.category;\n  if (raw === undefined || raw === null) return [DEFAULT_CATEGORY];\n\n  const list = Array.isArray(raw) ? raw : [raw];\n  const out = [];\n  for (const c of list) {\n    if (typeof c !== 'string' || !CATEGORY_SLUG_RE.test(c.trim())) {\n      throw new Error(\n        `getonbrd: invalid category ${JSON.stringify(c)} — expected a slug like \"programming\" or \"machine-learning-ai\"`,\n      );\n    }\n    const slug = c.trim();\n    if (!out.includes(slug)) out.push(slug);\n  }\n  if (!out.length) {\n    throw new Error('getonbrd: `categories` is empty — omit it to use the \"programming\" default');\n  }\n  if (out.length > MAX_CATEGORIES) {\n    throw new Error(\n      `getonbrd: ${out.length} categories configured — cap is ${MAX_CATEGORIES} (each one costs up to max_pages requests)`,\n    );\n  }\n  return out;\n}\n\n/** @param {string} url */\nfunction assertGetonbrdUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`getonbrd: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`getonbrd: URL must use HTTPS: ${url}`);","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/providers/getonbrd.mjs#L47-L83","documentation":"After slug validation and deduplication, resolveCategories rejects an empty resulting list. An empty `categories` array would produce zero API requests, so the provider requires you to either omit the key (using DEFAULT_CATEGORY 'programming') or supply at least one valid slug.","triggerScenarios":"Configuring `categories: []` in the portal entry, or a config-merge/filter step that strips all entries before resolveCategories runs.","commonSituations":"Users assuming an empty array means 'all categories'; templating that renders an empty YAML list when no categories are set; programmatic filtering that removes everything.","solutions":["Remove the `categories` key entirely to fall back to the 'programming' default","Add at least one valid category slug to the list","Fix upstream templating/filtering that empties the array"],"exampleFix":"// before\ncategories: []\n// after\n# omit the key, or:\ncategories: ['programming']","handlingStrategy":"validation","validationCode":"const cats = Array.isArray(cfg.categories) ? cfg.categories : undefined;\nif (cats && cats.length === 0) {\n  console.warn('categories is empty; omitting key to use default \"programming\"');\n  delete cfg.categories;\n}","typeGuard":"function hasNonEmptyCategories(entry) {\n  return !Array.isArray(entry.categories) || entry.categories.length > 0;\n}","tryCatchPattern":"try {\n  const jobs = await getonbrdProvider.fetch(entry, ctx);\n} catch (e) {\n  if (String(e.message).includes('`categories` is empty')) {\n    console.warn('Empty categories list for', entry.name, '— falling back to default category');\n    return getonbrdProvider.fetch({ ...entry, categories: undefined }, ctx);\n  }\n  throw e;\n}","preventionTips":["To mean 'all/default', omit the key instead of passing []","Guard template rendering so unset categories are omitted, not emitted as empty lists","Assert config invariants at load time"],"tags":["configuration","validation"],"backgroundTag":"empty-config-list","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}