{"record":{"id":"1339fa8996f92152","repo":"pbakaus/impeccable","slug":"flag-requires-a-non-empty-glob-1339fa","errorCode":null,"errorMessage":"${flag} requires a non-empty glob","messagePattern":"(.+?) requires a non-empty glob","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skill/scripts/hook-admin.mjs","lineNumber":634,"sourceCode":"}\n\nfunction addIgnoreFile(cwd, args) {\n  const parsed = parseIgnoreFileArgs(args);\n  const glob = parsed.glob;\n  if (!glob) throw new Error(`Pass a glob, e.g. ${IMPECCABLE_COMMAND} hooks ignore-file \"src/legacy/**\"`);\n  const config = mergeDetectorConfig(readRawDetectorConfig(cwd, { local: parsed.local }));\n  if (!config.ignoreFiles.includes(glob)) config.ignoreFiles.push(glob);\n  const target = writeDetectorConfig(cwd, config, { local: parsed.local });\n  const scope = parsed.local ? 'local detector.ignoreFiles' : 'shared detector.ignoreFiles';\n  return `Added \"${glob}\" to ${scope} (${path.relative(cwd, target) || target}). Current: ${config.ignoreFiles.join(', ')}`;\n}\n\n// An empty glob used to be dropped by filter(Boolean), so `--file=` reported\n// success and wrote an entry with no files: the user asked to scope a rule to one\n// file and silently got the project-wide suppression instead. Refuse it.\nfunction requireGlob(raw, flag) {\n  const glob = String(raw ?? '').trim();\n  if (!glob) throw new Error(`${flag} requires a non-empty glob`);\n  // A following flag is not a glob. `--file --reason \"why\"` consumed `--reason`\n  // as the scope and left the reason text to fold into the value, storing\n  // value=\"* why\" files=[\"--reason\"] and reporting success. Same silent-no-op\n  // class as an unknown flag folding into the value; refuse it the same way.\n  if (glob.startsWith('--')) throw new Error(`${flag} requires a glob, got the flag ${glob}`);\n  return glob;\n}\n\nfunction parseIgnoreValueArgs(args) {\n  const positionals = [];\n  const files = [];\n  let shared = false;\n  let local = false;\n  let reason = '';\n\n  for (let i = 0; i < args.length; i++) {\n    const arg = String(args[i] || '');\n    if (arg === '--shared') {","sourceCodeStart":616,"sourceCodeEnd":652,"githubUrl":"https://github.com/pbakaus/impeccable/blob/d14711ae3d1a1dd62dee61a358d27f107c51ccd0/skill/scripts/hook-admin.mjs#L616-L652","documentation":"Thrown by requireGlob() in skill/scripts/hook-admin.mjs when an ignore-value/ignore-rule file-scope flag (--file / --files / --file=) is given an empty or whitespace-only glob. Impeccable refuses empty globs because an older version silently dropped them via filter(Boolean), writing a suppression entry with no files and giving the user project-wide suppression instead of a one-file scope.","triggerScenarios":"Calling `impeccable hooks ignore-value <rule> <value> --file=` (trailing equals, no value), `--file \"\"`, `--file \"   \"`, or `--file` as the last argument with nothing after it. Also `--files=` empty.","commonSituations":"Shell quoting mistakes where a variable expands to empty (`--file=\"$MY_FILE\"` with MY_FILE unset), copy-pasting a command and deleting the path, or scripting the CLI without checking that the glob variable is set.","solutions":["Supply a non-empty glob after --file, e.g. `--file \"src/widget.css\"` or `--file \"src/**/*.css\"`.","If the path comes from a shell variable, guard it first: `[ -n \"$FILE\" ] && ... --file \"$FILE\"`.","For a single file use its concrete path; for a group use a brace/asterisk glob quoted to prevent shell expansion."],"exampleFix":"// before\nimpeccable hooks ignore-value overused-font Inter --file=\n// after\nimpeccable hooks ignore-value overused-font Inter --file \"src/widget.css\"","handlingStrategy":"validation","validationCode":"// Before building the CLI args, ensure the glob is a non-empty string.\nfunction buildFileArg(glob) {\n  if (typeof glob !== 'string' || glob.trim() === '' || glob.startsWith('--')) {\n    throw new Error(`Refusing to emit --file with invalid glob: ${JSON.stringify(glob)}`);\n  }\n  return ['--file', glob];\n}","typeGuard":"function isValidGlob(v) {\n  return typeof v === 'string' && v.trim() !== '' && !v.trim().startsWith('--');\n}","tryCatchPattern":null,"preventionTips":["Quote the glob so the shell cannot drop or split it.","Assert shell variables are non-empty before interpolating into --file.","Prefer the --file=<glob> equals form, which cannot swallow the next token."],"tags":["cli","validation","ignore-config","impeccable-hooks"],"backgroundTag":null,"analyzedSha":"d14711ae3d1a1dd62dee61a358d27f107c51ccd0","analyzedAt":"2026-08-13T00:52:25.771Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}