pbakaus/impeccable · error

requires a non-empty glob

Error message

${flag} requires a non-empty glob

What it means

Thrown by requireGlob when a --file/--files flag for `hooks ignore-value` receives an empty value after trimming. Historically filter(Boolean) dropped the empty entry, so `--file=` reported success but stored a value ignore with no file scope — the user asked to scope a suppression and silently got the broader one. The guard makes that failure loud.

Solutions

  1. Provide a real glob: `--file "src/widget.js"` or `--file="src/widget.js"`.
  2. Drop the --file flag entirely if you want the ignore to apply across the whole config's scope.
  3. In scripts, skip emitting the flag when the variable is empty rather than passing an empty value.

Example fix

# before
impeccable hooks ignore-value low-contrast "#eee" --file=

# after
impeccable hooks ignore-value low-contrast "#eee" --file "src/widget.css"
Defensive patterns

Strategy: validation

Validate before calling

function fileFlag(scope) {
  const v = String(scope ?? '').trim();
  return v ? ['--file', v] : [];   // omit the flag instead of passing an empty value
}

Type guard

function isNonEmptyGlob(v) {
  return typeof v === 'string' && v.trim().length > 0 && !v.trim().startsWith('--');
}

Prevention

When it happens

Trigger: `hooks ignore-value low-contrast "#eee" --file=` (empty equals-value); `--file ""` (explicitly empty string); a shell variable that expands to nothing: `--file "$SCOPE"` with SCOPE unset.

Common situations: Scripted invocations interpolating optional variables that are empty; hand-typed commands where the glob was forgotten after the =.

Related errors


AI-assisted analysis of pbakaus/impeccable@f88b2837a7 (2026-08-18). Data as JSON: /api/errors/1339fa8996f92152. Report an issue: GitHub.

Appendix: source

Thrown at skill/scripts/hook-admin.mjs:634

}

function addIgnoreFile(cwd, args) {
  const parsed = parseIgnoreFileArgs(args);
  const glob = parsed.glob;
  if (!glob) throw new Error(`Pass a glob, e.g. ${IMPECCABLE_COMMAND} hooks ignore-file "src/legacy/**"`);
  const config = mergeDetectorConfig(readRawDetectorConfig(cwd, { local: parsed.local }));
  if (!config.ignoreFiles.includes(glob)) config.ignoreFiles.push(glob);
  const target = writeDetectorConfig(cwd, config, { local: parsed.local });
  const scope = parsed.local ? 'local detector.ignoreFiles' : 'shared detector.ignoreFiles';
  return `Added "${glob}" to ${scope} (${path.relative(cwd, target) || target}). Current: ${config.ignoreFiles.join(', ')}`;
}

// An empty glob used to be dropped by filter(Boolean), so `--file=` reported
// success and wrote an entry with no files: the user asked to scope a rule to one
// file and silently got the project-wide suppression instead. Refuse it.
function requireGlob(raw, flag) {
  const glob = String(raw ?? '').trim();
  if (!glob) throw new Error(`${flag} requires a non-empty glob`);
  // A following flag is not a glob. `--file --reason "why"` consumed `--reason`
  // as the scope and left the reason text to fold into the value, storing
  // value="* why" files=["--reason"] and reporting success. Same silent-no-op
  // class as an unknown flag folding into the value; refuse it the same way.
  if (glob.startsWith('--')) throw new Error(`${flag} requires a glob, got the flag ${glob}`);
  return glob;
}

function parseIgnoreValueArgs(args) {
  const positionals = [];
  const files = [];
  let shared = false;
  let local = false;
  let reason = '';

  for (let i = 0; i < args.length; i++) {
    const arg = String(args[i] || '');
    if (arg === '--shared') {

View on GitHub (pinned to f88b2837a7)