santifer/career-ops · error · Error

unknown source in --sources

Error message

unknown source in --sources: ${source}

What it means

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.

Solutions

  1. Use only: techcrunch, prnewswire, guardian, hn (comma-separated).
  2. Replace display names with canonical slugs: 'Hacker News' → hn, 'The Guardian' → guardian.
  3. Check spelling/normalization: hyphens, lowercase, no spaces.
  4. Omit --sources entirely to fall back to DEFAULT_SOURCES.

Example fix

// before
node company-funded.mjs --sources techcrunch,hacker-news

// after
node company-funded.mjs --sources techcrunch,hn
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED = new Set(['techcrunch', 'prnewswire', 'guardian', 'hn']);
const CANON = { 'hacker news': 'hn', 'hackernews': 'hn', 'the guardian': 'guardian', 'pr newswire': 'prnewswire' };
function validSources(csv) {
  const list = csv.split(',').map(s => CANON[s.trim().toLowerCase()] || s.trim().toLowerCase());
  const bad = list.filter(s => !ALLOWED.has(s));
  return bad.length ? { ok: false, bad } : { ok: true, list };
}

Type guard

const isAllowedSource = (s) => ['techcrunch', 'prnewswire', 'guardian', 'hn'].includes(s);

Try / catch

try {
  run(['--sources', sources]);
} catch (e) {
  if (e.message.startsWith('unknown source in --sources:')) {
    console.warn(`${e.message} — falling back to default sources`);
    run([]); // no --sources → DEFAULT_SOURCES
  } else throw e;
}

Prevention

When it happens

Trigger: `--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.

Common situations: 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.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/81992b2bd986f778. Report an issue: GitHub.

Appendix: source

Thrown at company-funded.mjs:146

  }
  if (!Number.isInteger(opts.months) || opts.months <= 0 || opts.months > 120) {
    throw new Error('--months must be an integer from 1 to 120');
  }
  if (!['date', 'score'].includes(opts.sort)) {
    throw new Error('--sort must be date or score');
  }
  return opts;
}

function parseSources(value) {
  const out = String(value || '')
    .split(',')
    .map((s) => normalizeSourceName(s.trim()))
    .filter(Boolean);
  const unique = [...new Set(out)];
  const allowed = new Set(['techcrunch', 'prnewswire', 'guardian', 'hn']);
  for (const source of unique) {
    if (!allowed.has(source)) throw new Error(`unknown source in --sources: ${source}`);
  }
  return unique.length ? unique : [...DEFAULT_SOURCES];
}

function normalizeSourceName(value) {
  const s = String(value || '').toLowerCase().replace(/[_\s]+/g, '-');
  if (s === 'hacker-news' || s === 'hackernews' || s === 'hn') return 'hn';
  return s.replace(/-/g, '');
}

function compact(text) {
  return String(text || '').replace(/\s+/g, ' ').trim();
}

function companySlug(name) {
  return compact(name)
    .toLowerCase()
    .replace(/&/g, ' and ')

View on GitHub (pinned to aac998c7ed)