Skip to content

fix: catch EOFError in install prompts and fall back to defaults - #2130

Open
hippi345 wants to merge 3 commits into
jrnl-org:mainfrom
hippi345:fix/install-prompt-eof-defaults
Open

hippi345 wants to merge 3 commits into
jrnl-org:mainfrom
hippi345:fix/install-prompt-eof-defaults

Conversation

@hippi345

@hippi345 hippi345 commented Oct 8, 2026 •

Copy link
Copy Markdown

Fixes #2125

When jrnl is run with stdin closed or redirected (e.g. jrnl < /dev/null, in scripts, or in non-interactive CI), the first-run install prompts raise EOFError and crash with a traceback instead of completing with sensible defaults.

The fix catches EOFError only around the three install-time prompts in install() (jrnl/install.py), and each falls back to its declared default:

  • Journal path: falls back to the platform default path
  • Encrypt? (default No): stays false
  • Colors? (default Yes): colors are applied

The shared print_msgs() helper is unchanged, so password prompts and all other interactive prompts still propagate EOFError. jrnl --encrypt < /dev/null therefore can't set an empty password. KeyboardInterrupt is not caught.

If the user answers yes to encryption and stdin then reaches EOF at the colors prompt, install() re-raises EOFError before save_config(), so an encrypt: true config is never written without a password (which would otherwise fail on every later run with ValueError: Invalid IV size (0) for CBC). This matches the pre-fix behavior for that case, so the user can simply retry.

Tests:

  • test_install_uses_defaults_when_stdin_is_eof injects EOFError into the console input mock and asserts that install() completes with the expected defaults. It fails with EOFError without the fix.
  • test_password_prompt_propagates_eof asserts that the password prompt still raises EOFError on EOF, so the fix can't widen into an empty-password path.
  • test_install_eof_after_yes_to_encrypt_aborts_without_writing_config asserts that EOF after answering yes to encrypt raises and writes no config. The existing EOF test also asserts that colors default to on.

…faults

When jrnl is invoked with stdin closed or redirected from /dev/null, the
interactive first-run install prompts raise EOFError.  Catch that error in
the shared print_msgs() helper in jrnl/output.py and return an empty string
so every prompt falls back to its existing default (journal path, no
encryption, default colors).

KeyboardInterrupt is left unhandled and continues to propagate normally.

Adds a unit test that injects EOFError into the console input mock and
asserts that install() completes without raising and returns a config with
the expected defaults.
…msgs

The previous commit caught EOFError in the shared print_msgs() helper.
That was too broad: password prompts (create_password, prompt_password)
call the same helper and would silently return "" on EOF, allowing an
empty password to be set instead of surfacing the error to the caller.

Remove the catch from print_msgs() and instead wrap only the three
install-time prompts in install() with targeted try/except EOFError
blocks, each falling back to that prompt's declared default:
- journal path prompt  -> empty string (uses get_default_journal_path())
- encrypt question     -> False
- colors question      -> True

Password prompts and all other interactive input continue to propagate
EOFError unchanged.

Add test_password_prompt_propagates_eof to assert that prompt_password()
raises EOFError when stdin is at EOF.  This test fails against the
previous commit (55abe86) because the broad print_msgs catch swallowed
the error and returned "" instead.
… config

When stdin runs out after the user answers encrypt=yes (e.g. printf '\ny\n'
| jrnl), the previous commit swallowed EOF at the colors prompt and then
called save_config() with encrypt:true and no password set.  Every
subsequent invocation would fail with ValueError: Invalid IV size (0) for
CBC, bricking the journal permanently.

Fix: in the colors-prompt except-EOFError block, re-raise when encrypt is
True.  EOFError then propagates out of install() before save_config() is
called, so no config or journal file is written.  The user can simply retry
with a fully connected stdin.

The two existing EOF defaults are unchanged:
- journal path EOF -> default path (encrypt still False at that point)
- encrypt EOF     -> False, so the colors EOF-default (True) is still safe

Add test_install_eof_after_yes_to_encrypt_aborts_without_writing_config
which supplies blank/y/EOF to the three install prompts and asserts that
install() raises EOFError and leaves no config file on disk.  This test
fails against c7a3755 with 'DID NOT RAISE EOFError'.

Also extend test_install_uses_defaults_when_stdin_is_eof to assert that
the colors config defaults to ON (date:black, tags:yellow, title:cyan)
when all prompts receive EOF.
@hippi345
hippi345 marked this pull request as ready for review October 8, 2026 21:29

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant