Skip to content

Completion-cache write failures are silent: an unwritable cache dir kills completion with no diagnostic #181

Description

@Sootopolis

Sibling of the read-side fix on feat/club-cli-ux (#173 follow-up). CompletionCache's four write paths — writeClubsIn, seedClubsIn, appendJobIn, invalidateIn — all end in .ignore. If the cache directory is unwritable, the cache never populates, silently, forever: shell completion simply does nothing and nothing ever says why.

Reproduced (no server needed):

XDG_CACHE_HOME=$BASE/cache            # writable root
mkdir -p $XDG_CACHE_HOME/ccas && chmod 500 $XDG_CACHE_HOME/ccas
# config.conf: default_clubs = ["team-alpha", "team-beta"]
$ ccas use-club
team-alpha                            # exit 0, no mention of the cache
$ ls $XDG_CACHE_HOME/ccas/            # empty — clubs.txt was never created

seedClubs attempted the write, failed, and said nothing.

The read-side Option fix does not cover this. That distinguishes "unreadable" from "empty", but here the file is absent, so readClubsIn correctly returns Some(Nil) and correctly stays quiet. The gap is purely on the write path.

Realistic trigger: a root-owned ~/.cache/ccas left behind by a single sudo ccas run (same root cause as the unreadable-file case), a read-only home, or a misconfigured XDG_CACHE_HOME.

Recommended shape

Do not warn at every write site. seedClubs runs on every invocation (Main.scala:46, before parsing), so a broken dir would nag on ccas --help, ccas config get, and every other command.

Surface a failure only where the cache write is semantically part of what the user just asked for:

  • use-club <slug> — the user is setting up club context; the refresh is part of making it work.
  • club add / club remove — these mutate the managed set, which is the cache's source.

Stay silent on incidental writes: the post-command .tap refresh in Dispatcher, appendJob, seedClubs.

Mechanically that means only writeClubs (and arguably invalidate) stop being .ignore and return a success signal; those two or three call sites decide whether to emit one stderr line. Never change an exit code — a broken cache is not a failed command.

Cheap complementary option worth considering in the same issue: have ccas completion <shell> check cache-dir writability as it emits the script and warn on stderr if not writable. That is exactly the moment the user is installing completion, costs one stat, and adds no noise anywhere else — though it only catches setup-time breakage, not a dir that breaks later.

Also worth deciding

clubsStaleIn's .orElseSucceed(true) has the same error-vs-normal conflation but is fine as-is — an error maps to "stale", which just triggers a refresh. That is the safe default and self-corrects, so no information is lost. Recommend leaving it and noting why.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:cliCLI binary (subcommands, completions, config, packaging)bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions