Today —
$ printf 'not json' > nj.json
$ printf '{"data":{}}' > nodata.json
$ printf '{"data":{"user":{"login":"x"}}}' > nocal.json
$ : > empty.json
$ for f in nj.json nodata.json nocal.json empty.json; do echo "--- $f"; mossaic --file $f --png o.png </dev/null 2>&1 | head -1; done
--- nj.json
mossaic: unexpected response from gh: expected ident at line 1 column 2
--- nodata.json
mossaic: GitHub returned no user for that login
--- nocal.json
mossaic: unexpected response from gh: missing field `contributionsCollection` at line 1 column 29
--- empty.json
mossaic: unexpected response from gh: EOF while parsing a value at line 1 column 0
$ env PATH=/usr/bin:/bin mossaic --file nj.json --png o.png </dev/null 2>&1 | head -1
mossaic: unexpected response from gh: expected ident at line 1 column 2
Identical with gh off PATH: it is never executed on this path. The tracker's --merge repeats it, one half-step better:
$ mossaic-art VYNCINT --year 2026 --track --merge $PWD/nj.json … | head -1
mossaic-art: could not read "…/nj.json": unexpected response from gh: expected ident at line 1 column 2
The plan loader, two commands away, gets it right:
$ mossaic-art --track --plan $PWD/nj.json --no-colour 2>&1 | head -1
mossaic-art: /…/nj.json is not a mossaic plan: expected ident at line 1 column 2
One shared parse is why: src/github.rs:46 reads the file and hands the body to the same parse the network path uses, whose only wording is format!("unexpected response from gh: {e}") at src/github.rs:98.
Why it is worth fixing — --file is the offline entry point the whole test story rests on: install.yml, docs/DESIGN.md §13 and the --snapshot round trip all lean on it. When it goes wrong the user is told their GitHub CLI returned something odd — and, for {"data":{}}, that GitHub has no such login — for a local file they wrote themselves, on a run where gh never ran. They then check gh auth status, the username and the network: everything except the truncated JSON in front of them. --file is also the flag most likely to be handed a file another process is still writing, which is exactly the truncated case. 0.6.3 fixed the neighbour (mossaic --file typo.json now says "could not read typo.json: No such file or directory") and stopped at the missing-file case.
To be accurate about the standard: there is no written rule anywhere in AGENTS.md, CONTRIBUTING.md or docs/DESIGN.md about error messages naming the right subsystem. The case here is the factual one — the message names a subprocess that did not run — plus the inconsistency with the project's own plan loader.
Fix — give github::from_file its own wording instead of reusing the fetch's. Thread the source through parse — an enum of Gh or File(path) is enough — so the messages read mossaic: <path> is not a saved contributions response: expected ident at line 1 column 2 and mossaic: <path> holds no user — a saved response has data.user, and drop the "from gh" phrasing from the chart's and the tracker's file paths. Mirror the plan loader, which already does this well. While there, --merge quotes the path where every other path message does not; make them agree.
Done when — mossaic --file on a non-JSON file, an empty file, a truncated file and a valid-JSON-wrong-shape file each name the path, do not mention gh, and exit 2; the same four through mossaic-art --merge do too; and a test asserts that for a file-sourced parse failure the path appears in the message and the substring "gh" does not.
Today —
Identical with
ghoff PATH: it is never executed on this path. The tracker's--mergerepeats it, one half-step better:The plan loader, two commands away, gets it right:
One shared
parseis why: src/github.rs:46 reads the file and hands the body to the sameparsethe network path uses, whose only wording isformat!("unexpected response from gh: {e}")at src/github.rs:98.Why it is worth fixing —
--fileis the offline entry point the whole test story rests on: install.yml, docs/DESIGN.md §13 and the--snapshotround trip all lean on it. When it goes wrong the user is told their GitHub CLI returned something odd — and, for{"data":{}}, that GitHub has no such login — for a local file they wrote themselves, on a run where gh never ran. They then checkgh auth status, the username and the network: everything except the truncated JSON in front of them.--fileis also the flag most likely to be handed a file another process is still writing, which is exactly the truncated case. 0.6.3 fixed the neighbour (mossaic --file typo.jsonnow says "could not read typo.json: No such file or directory") and stopped at the missing-file case.To be accurate about the standard: there is no written rule anywhere in AGENTS.md, CONTRIBUTING.md or docs/DESIGN.md about error messages naming the right subsystem. The case here is the factual one — the message names a subprocess that did not run — plus the inconsistency with the project's own plan loader.
Fix — give
github::from_fileits own wording instead of reusing the fetch's. Thread the source throughparse— an enum ofGhorFile(path)is enough — so the messages readmossaic: <path> is not a saved contributions response: expected ident at line 1 column 2andmossaic: <path> holds no user — a saved response has data.user, and drop the "from gh" phrasing from the chart's and the tracker's file paths. Mirror the plan loader, which already does this well. While there,--mergequotes the path where every other path message does not; make them agree.Done when —
mossaic --fileon a non-JSON file, an empty file, a truncated file and a valid-JSON-wrong-shape file each name the path, do not mention gh, and exit 2; the same four throughmossaic-art --mergedo too; and a test asserts that for a file-sourced parse failure the path appears in the message and the substring "gh" does not.