{"record":{"id":"d8c711e0b2bc0687","repo":"santifer/career-ops","slug":"getonbrd-out-length-categories-configured-ca","errorCode":null,"errorMessage":"getonbrd: ${out.length} categories configured — cap is ${MAX_CATEGORIES} (each one costs up to max_pages requests)","messagePattern":"getonbrd: (.+?) categories configured — cap is (.+?) \\(each one costs up to max_pages requests\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/getonbrd.mjs","lineNumber":68,"sourceCode":"  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}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`getonbrd: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_HOST}`);\n  }","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/providers/getonbrd.mjs#L50-L86","documentation":"resolveCategories enforces MAX_CATEGORIES because every configured category costs up to max_pages API requests per scan. Configuring more categories than the cap is rejected to protect rate limits and scan runtime.","triggerScenarios":"A portal entry listing more than MAX_CATEGORIES valid, deduplicated slugs — e.g. copying the full getonbrd category index into one entry.","commonSituations":"Users trying to scan 'everything' at once; merging configs from multiple sources that concatenate category lists; forgetting dedup happens before the check (duplicates don't save you).","solutions":["Trim the list to the MAX_CATEGORIES most relevant slugs (the cap value is in the message)","Split the workload across multiple portal entries or scheduled runs","Increase MAX_CATEGORIES in the provider only if you accept the extra request cost per category"],"exampleFix":"// before\ncategories: ['programming', 'design', 'data-science', 'devops', 'product', 'marketing', 'sales', 'finance']\n// after (cap-aware)\ncategories: ['programming', 'data-science', 'devops']","handlingStrategy":"validation","validationCode":"const MAX = 3; // mirror provider MAX_CATEGORIES\nif ((entry.categories ?? []).length > MAX) {\n  entry.categories = entry.categories.slice(0, MAX);\n  console.warn('Trimmed getonbrd categories to cap of', MAX);\n}","typeGuard":"function isWithinCategoryCap(entry, cap) {\n  return !Array.isArray(entry.categories) || entry.categories.length <= cap;\n}","tryCatchPattern":"try {\n  const jobs = await getonbrdProvider.fetch(entry, ctx);\n} catch (e) {\n  if (/getonbrd: \\d+ categories configured/.test(String(e.message))) {\n    console.warn('Too many categories for', entry.name, '— reduce to the cap and rescan');\n    return [];\n  }\n  throw e;\n}","preventionTips":["Prioritize the few highest-value categories per portal entry instead of listing many","Spread broad coverage across multiple scheduled entries/runs","Check the provider's MAX_CATEGORIES before generating config programmatically"],"tags":["configuration","validation","rate-limit"],"backgroundTag":"config-limit-exceeded","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}