{"record":{"id":"1eaf0f7298c24e8e","repo":"santifer/career-ops","slug":"file-not-found-filepath","errorCode":null,"errorMessage":"file not found: ${filePath}","messagePattern":"file not found: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"validate-portals.mjs","lineNumber":277,"sourceCode":"        } else if (!providerIds.has(company.provider)) {\n          add(errors, `${base}.provider`, `unknown provider \"${company.provider}\"`);\n        }\n      }\n\n      validateParser(company.parser, `${base}.parser`, errors);\n    }\n  }\n\n  return { errors, warnings };\n}\n\nfunction formatIssue(issue) {\n  return `${issue.path}: ${issue.message}`;\n}\n\nasync function validateFile(filePath) {\n  if (!existsSync(filePath)) {\n    throw new Error(`file not found: ${filePath}`);\n  }\n  const providerIds = await loadProviderIds();\n  const parsed = yaml.load(readFileSync(filePath, 'utf-8'));\n  return validatePortalsConfig(parsed, { providerIds });\n}\n\nasync function runSelfTest() {\n  const tmp = mkdtempSync(join(tmpdir(), 'career-ops-validate-portals-self-test-'));\n  try {\n    const file = join(tmp, 'bad.yml');\n    writeFileSync(file, `\ntitle_filter:\n  positive: [\"AI\", \"\"]\ntracked_companies:\n  - name: \"Acme\"\n    provider: \"not-real\"\n    careers_url: \"https://jobs.lever.co/acme\"\n`, 'utf-8');","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/validate-portals.mjs#L259-L295","documentation":"validate-portals.mjs's validateFile() runs existsSync() on the resolved config path and throws this before parsing when the file is absent. main() catches it, prints 'validate-portals failed: file not found: <path>' and exits 1. Note the empty-`--file=` guard: an explicitly empty flag value is turned into a usage error instead of resolving to the current directory.","triggerScenarios":"`node validate-portals.mjs` before onboarding has created portals.yml (DEFAULT_PORTALS_PATH is missing), or `--file <path>` with a typo'd/relative path resolved against the wrong cwd. Self-test writes its own temp file, so it never hits this.","commonSituations":"Fresh clone where portals.yml has not yet been copied from templates/portals.example.yml (Step 3 of onboarding); running the command from a subdirectory so the relative default path misses; CI validating a renamed config path.","solutions":["Confirm the file exists at the path printed in the message: `ls -l <path>`","Run from the repo root, or pass an absolute path: `node validate-portals.mjs --file /abs/path/portals.yml`","If portals.yml is missing, create it first: `cp templates/portals.example.yml portals.yml` then edit","Check for typos in the --file value and remember the path resolves against process.cwd()"],"exampleFix":"# before (run from scripts/ subdir; portals.yml lives at repo root)\nnode ../validate-portals.mjs   # file not found: /repo/scripts/portals.yml\n# after\nnode ../validate-portals.mjs --file /repo/portals.yml","handlingStrategy":"validation","validationCode":"import { existsSync, realpathSync } from 'node:fs';\nimport { resolve } from 'node:path';\nconst p = resolve(process.cwd(), 'portals.yml');\nif (!existsSync(p)) {\n  console.error(`portals.yml missing at ${p}; copy templates/portals.example.yml first`);\n  process.exit(1);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await validateFile(filePath);\n} catch (err) {\n  if (String(err.message).startsWith('file not found:')) {\n    // path problem, not schema problem: fix the path, do not touch the config\n    console.error('bad path, expected:', err.message);\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["Run validate-portals from the repo root, or always pass --file with an absolute path","Finish onboarding (Step 3 copies templates/portals.example.yml to portals.yml) before validating","Remember --file resolves against cwd, and an empty --file= is a usage error by design"],"tags":["file-not-found","cli","config","onboarding"],"backgroundTag":"file-not-found","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}