{"record":{"id":"80915fd52f7d2ce7","repo":"santifer/career-ops","slug":"local-parser-parser-command-is-required","errorCode":null,"errorMessage":"local-parser: parser.command is required","messagePattern":"local-parser: parser\\.command is required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/local-parser.mjs","lineNumber":94,"sourceCode":"  if (Array.isArray(parser.args)) args.push(...parser.args);\n\n  return args.map(arg => expandParserArg(arg, entry));\n}\n\n// Resolve a configured path and confirm it stays inside the project tree.\nfunction resolveInsideRoot(rawPath) {\n  const resolved = realpathSync(resolve(PROJECT_ROOT, String(rawPath)));\n  if (resolved !== PROJECT_ROOT && !resolved.startsWith(PROJECT_ROOT + sep)) {\n    throw new Error(`local-parser: path escapes the project root: ${rawPath}`);\n  }\n  return resolved;\n}\n\n// The command is either a whitelisted interpreter (resolved via PATH) or a script\n// that lives inside the repo. Anything else is rejected.\nfunction resolveCommand(command) {\n  const value = String(command || '');\n  if (!value) throw new Error('local-parser: parser.command is required');\n  if (!value.includes('/') && ALLOWED_INTERPRETERS.has(value)) return value;\n  return resolveInsideRoot(value);\n}\n\n// Validate the whole invocation and return what to spawn. Throws on anything unsafe.\nfunction resolveInvocation(entry) {\n  const rawCommand = String(entry.parser?.command || '');\n  const command = resolveCommand(rawCommand);\n  const args = buildParserArgs(entry);\n  const scriptPath = getParserScriptPath(entry);\n\n  const usesInterpreter = !rawCommand.includes('/') && ALLOWED_INTERPRETERS.has(rawCommand);\n  if (usesInterpreter) {\n    // A whitelisted interpreter must run an in-repo script as its FIRST argument.\n    // Anything before the script is an interpreter option (node --eval / --require,\n    // python -c, …) that could execute arbitrary code, so require the script to lead.\n    if (!scriptPath) throw new Error('local-parser: interpreter command requires an in-repo parser script');\n    resolveInsideRoot(scriptPath);","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/local-parser.mjs#L76-L112","documentation":"resolveCommand() requires a non-empty parser.command. The command must be either a whitelisted interpreter name (python3, python, node, deno, bun, sh, bash, without slashes) or an in-repo file path; an empty/missing command fails immediately with this error.","triggerScenarios":"Calling runLocalParser/resolveInvocation on an entry where entry.parser exists but parser.command is undefined, null, empty string, or whitespace-only (String(command || '') collapses falsy values to '').","commonSituations":"YAML parser block written with only `args:` and no `command:`; command key misspelled (e.g. `cmd:`); command accidentally set to '' or ~ (null) after template substitution; detect() returning null and a caller invoking fetch() anyway through a different path.","solutions":["Add a command to the entry's parser block, e.g. parser: {command: node, args: [\"parsers/jobs.js\"]}.","Check the YAML key spelling — it must be exactly `command` under `parser`.","Ensure template/placeholder substitution didn't blank the value (inspect the rendered portals.yml).","Note detect() already returns null when parser.command is missing — make sure entry selection relies on detect() before fetch()."],"exampleFix":"// before (portals.yml)\nparser: {args: [\"parsers/jobs.js\"]}\n// after\nparser: {command: node, args: [\"parsers/jobs.js\"]}","handlingStrategy":"validation","validationCode":"if (!entry.parser || !String(entry.parser.command || '').trim()) {\n  throw new Error(`${entry.name}: parser.command is required (python3|node|... or an in-repo script path)`);\n}","typeGuard":"function hasParserCommand(entry) {\n  return typeof entry.parser === 'object' && entry.parser !== null && typeof entry.parser.command === 'string' && entry.parser.command.trim().length > 0;\n}","tryCatchPattern":"try {\n  await localParser.fetch(entry);\n} catch (e) {\n  if (String(e.message).includes('parser.command is required')) {\n    console.error(`${entry.name}: add parser: {command: node, args: [...]} to portals.yml`);\n    return [];\n  }\n  throw e;\n}","preventionTips":["Only select entries whose provider detect() returned non-null — it already rejects missing command.","Keep a parser block template with command filled in; never leave command blank after substitution.","Validate YAML keys (command, not cmd) when hand-editing portals.yml.","Add a config smoke test that resolves every parser invocation at startup."],"tags":["config","missing-field","validation"],"backgroundTag":"missing-required-config-field","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"}