{"record":{"id":"81992b2bd986f778","repo":"santifer/career-ops","slug":"unknown-source-in-sources-source","errorCode":null,"errorMessage":"unknown source in --sources: ${source}","messagePattern":"unknown source in --sources: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"company-funded.mjs","lineNumber":146,"sourceCode":"  }\n  if (!Number.isInteger(opts.months) || opts.months <= 0 || opts.months > 120) {\n    throw new Error('--months must be an integer from 1 to 120');\n  }\n  if (!['date', 'score'].includes(opts.sort)) {\n    throw new Error('--sort must be date or score');\n  }\n  return opts;\n}\n\nfunction parseSources(value) {\n  const out = String(value || '')\n    .split(',')\n    .map((s) => normalizeSourceName(s.trim()))\n    .filter(Boolean);\n  const unique = [...new Set(out)];\n  const allowed = new Set(['techcrunch', 'prnewswire', 'guardian', 'hn']);\n  for (const source of unique) {\n    if (!allowed.has(source)) throw new Error(`unknown source in --sources: ${source}`);\n  }\n  return unique.length ? unique : [...DEFAULT_SOURCES];\n}\n\nfunction normalizeSourceName(value) {\n  const s = String(value || '').toLowerCase().replace(/[_\\s]+/g, '-');\n  if (s === 'hacker-news' || s === 'hackernews' || s === 'hn') return 'hn';\n  return s.replace(/-/g, '');\n}\n\nfunction compact(text) {\n  return String(text || '').replace(/\\s+/g, ' ').trim();\n}\n\nfunction companySlug(name) {\n  return compact(name)\n    .toLowerCase()\n    .replace(/&/g, ' and ')","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/company-funded.mjs#L128-L164","documentation":"parseSources splits the comma-separated `--sources` value, normalizes each name (lowercase, underscores/spaces to hyphens), dedupes, and validates each against the allowlist {techcrunch, prnewswire, guardian, hn}. Unknown names throw this error naming the offending source.","triggerScenarios":"`--sources techcrunch,twitter`, `--sources hacker-news` (canonical short name is `hn`), a misspelled source like `techcrunsh`, or a source that exists in the wider project but isn't enabled for this script.","commonSituations":"Guessing source slugs instead of checking documentation; using display names ('Hacker News', 'The Guardian') that normalize outside the allowlist; scripts generating source lists from stale config; a source was added upstream but this script's allowlist wasn't updated.","solutions":["Use only: techcrunch, prnewswire, guardian, hn (comma-separated).","Replace display names with canonical slugs: 'Hacker News' → hn, 'The Guardian' → guardian.","Check spelling/normalization: hyphens, lowercase, no spaces.","Omit --sources entirely to fall back to DEFAULT_SOURCES."],"exampleFix":"// before\nnode company-funded.mjs --sources techcrunch,hacker-news\n\n// after\nnode company-funded.mjs --sources techcrunch,hn","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['techcrunch', 'prnewswire', 'guardian', 'hn']);\nconst CANON = { 'hacker news': 'hn', 'hackernews': 'hn', 'the guardian': 'guardian', 'pr newswire': 'prnewswire' };\nfunction validSources(csv) {\n  const list = csv.split(',').map(s => CANON[s.trim().toLowerCase()] || s.trim().toLowerCase());\n  const bad = list.filter(s => !ALLOWED.has(s));\n  return bad.length ? { ok: false, bad } : { ok: true, list };\n}","typeGuard":"const isAllowedSource = (s) => ['techcrunch', 'prnewswire', 'guardian', 'hn'].includes(s);","tryCatchPattern":"try {\n  run(['--sources', sources]);\n} catch (e) {\n  if (e.message.startsWith('unknown source in --sources:')) {\n    console.warn(`${e.message} — falling back to default sources`);\n    run([]); // no --sources → DEFAULT_SOURCES\n  } else throw e;\n}","preventionTips":["Keep a canonical slug list near where source selection is configured.","Prefer omitting --sources to accept DEFAULT_SOURCES.","Normalize display names to slugs before passing.","Update wrappers when new sources are added upstream."],"tags":["cli","validation","enum"],"backgroundTag":"invalid-enum-value","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}