Skip to content

validateDirectory exits 1 (policy failure) instead of 2 (invalid input) for missing/invalid directory #209

Description

@flaglint

What's wrong

`validateDirectory` in `src/commands/shared.ts` calls `process.exit(1)` for both "directory not found" and "not a directory" — verified against the built binary:

```
$ flaglint scan /no/such/directory
Error: Directory not found: /no/such/directory
$ echo $?
1

$ flaglint scan package.json
Error: Not a directory: package.json
$ echo $?
1

$ flaglint validate /no/such/directory
Error: Directory not found: /no/such/directory
$ echo $?
1
```

Why this is a bug, not just an inconsistency

The documented exit-code contract (ADR 010, CHANGELOG, the JSON output contract docs) is explicit: 0 success, 1 policy/staleness failure, 2 invalid user input (bad `--format`, missing/malformed config — a missing directory is squarely in this category), 3 internal error. `--format` validation already correctly exits 2 in the same commands, so the contract clearly intends directory errors to be exit 2 too — this looks like an oversight in `validateDirectory`, not a considered choice.

Two concrete problems this causes:

  1. `scan`/`audit` are documented (and were specifically fixed in v1.1.0, see the "scan command exit code" fix in the CHANGELOG) to never produce exit 1 — they're inventory commands, not policy gates. A bad directory silently reintroduces an exit-1 path for these commands.
  2. For `validate`, exit 1 is supposed to mean "found a policy violation" — a CI script branching on exit code can't currently distinguish "the codebase has direct LaunchDarkly usage" from "you typo'd the directory path."

Suggested fix

Change `validateDirectory`'s two `process.exit(1)` calls to `process.exit(2)`, matching the same-file `--format` validation that already does this correctly.

Context

Found while building flaglint-go (github.com/flaglint/flaglint-go), which needs to match this exit-code contract exactly for cross-tool CI interop (see that repo's ADR 003). flaglint-go intentionally implements the documented contract (exit 2 for a missing/invalid directory) rather than replicating this bug, so until this is fixed there's a small, known behavioral mismatch between the two tools for this one case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions