gabriel / musehub public
Open #213 Bug
filed by gabriel human · 33 days ago

HttpTransport.hub_json() silently discards valid JSON array responses, returning {} with no warning

0 Anchors
— Blast radius
— Churn 30d
0 Proposals
No description provided.
◎ Activity1
●
gabriel opened this issue 33 days ago
○
gabriel 33 days ago

Confirmed while building Episode 20 ("Mist") demo material. Real, silent, no-error-signaled data loss on the client side — distinct from staging#212 (mist push crash), found in the same episode's research pass.

Repro:

# server-side, confirmed correct via raw curl + hand-signed header:
$ curl .../api/mists/Es8ujVEEAd2c/forks -H "Authorization: ..."
[{"mistId":"Hq4kz66XdNnR","owner":"gabriel","forkParentId":"Es8ujVEEAd2c",...}]

# same data, via the CLI:
$ muse mist forks gabriel/Es8ujVEEAd2c --json
{}

The server returns a genuine, correct, non-empty JSON array. The CLI prints an empty JSON object. No error, no warning, exit code 0 — indistinguishable from "this mist genuinely has zero forks," even though forkCount: 1 on the same mist (confirmed via muse mist read) proves the fork is real and the server-side data is intact.

Root cause: muse/core/transport.py::HttpTransport.hub_json():

result = _json_mod.loads(raw.decode("utf-8"))
...
if not isinstance(result, dict):
    return {}
return result

Any endpoint whose top-level JSON response is a list (as GET /api/mists/{id}/forks — and per this function's own return-type annotation, -> "dict[str, object]", this was apparently never expected to happen) gets silently coerced to {}. mist.py::run_forks() then does print(json.dumps(data)) for the --json path with no further validation, propagating the empty dict straight to output.

Blast radius beyond forks: hub_json() is the shared transport helper for every muse mist * (and likely other) subcommand that talks to MuseHub's JSON API. Any other endpoint that legitimately returns a top-level array — not just /forks — would hit the exact same silent discard. Worth auditing all _hub_api()/hub_json() call sites for endpoints with array response bodies, not just fixing forks.

Suggested fix: change hub_json()'s return type to dict[str, object] | list[object] (or a union JSON-value type) and remove the isinstance coercion entirely — let callers handle the shape they expect, the same way run_forks() already has a (currently unreachable) data if isinstance(data, list) else data.get("forks", []) fallback for the non---json render path, suggesting the original author anticipated array responses but the transport layer was never updated to allow them through.

Deferred per usual until after the season wraps.