Skip to content

fix: graceful ENOENT error when DESIGN.md is missing (#132)#145

Open
Emp1500 wants to merge 2 commits into
google-labs-code:mainfrom
Emp1500:fix/enoent-friendly-error
Open

fix: graceful ENOENT error when DESIGN.md is missing (#132)#145
Emp1500 wants to merge 2 commits into
google-labs-code:mainfrom
Emp1500:fix/enoent-friendly-error

Conversation

@Emp1500

@Emp1500 Emp1500 commented Jun 28, 2026

Copy link
Copy Markdown

fix(cli): graceful error on missing or unreadable input file

##Before
ERROR ENOENT: no such file or directory, open 'DESIGN.md'
at readFileSync (node:fs:441:20)
at readInput (...index.js:24123:12)

##After

File missing:
Error: "DESIGN.md" not found. Create a DESIGN.md file or pass "-" to read from stdin.

Permission denied:
Error: "DESIGN.md" could not be read: permission denied.

Any other I/O error:
Error: "DESIGN.md" could not be read:

##Solution

Introduces a typed FileReadError class. readInput now throws it instead of calling process.exit directly. A friendlyMessage getter on the class checks the underlying OS error code
(ENOENT, EACCES, etc.) to produce an accurate message — so the error never says "not found" when the real problem is permissions.

Each command catches FileReadError, writes the plain-text message to stderr, and exits with code 2.

##Why this approach is better than a plain process.exit wrap:

  • readInput is unit-testable without mocking process.exit
  • FileReadError.filePath pinpoints which file failed (matters for diff, which reads two files)
  • FileReadError.friendlyMessage centralises message logic in one place — commands stay clean
  • Exit-code policy stays in the command layer where it belongs

##Changes

  • utils.ts — FileReadError class with friendlyMessage getter + readInput throws instead of exits
  • utils.test.ts — 6 new tests for readInput and FileReadError, zero mocking required
  • commands/lint.ts, commands/diff.ts, commands/export.ts — catch FileReadError, write error.friendlyMessage to stderr, exit code 2

Fixes #132

Emp1500 added 2 commits June 28, 2026 04:14
… stderr (google-labs-code#132)

Introduce a typed `FileReadError` class in `readInput` so the function
throws instead of calling `process.exit` directly. Each command handler
catches it and writes a plain-text error to stderr:

  Error: "DESIGN.md" not found.
  Create a DESIGN.md file or pass "-" to read from stdin.

This replaces the unhandled Node.js stack trace dump reported in google-labs-code#132.
`readInput` is now unit-testable without mocking `process.exit`, and
`FileReadError.filePath` identifies the specific missing file (important
for `diff`, which reads two files).
Replace the hardcoded "not found" string in all command handlers with
a friendlyMessage getter on FileReadError that checks the OS error code:
- ENOENT → "not found. Create a DESIGN.md file or pass '-' for stdin."
- EACCES → "could not be read: permission denied."
- other  → "could not be read: <raw message>"

This prevents a misleading "not found" message when the file exists
but cannot be read due to permissions or other I/O errors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant