fix(tools,tui_gateway,cli,plugins): parseable non-dict JSON no longer crashes the remaining file scans
Same class as the Bot Chat drain wedge already on this branch: every JSON-file scan guarded "did it parse?" and then assumed the value was a dict. A file holding `42`, `"oops"` or `[1,2,3]` (corruption, truncated write, foreign tool) passed the guard and raised AttributeError/TypeError at the first `.get()`, usually before a single healthy sibling was processed. Each site now treats a non-object payload like a corrupt file under that subsystem's existing policy: - tools/bot_relay.py::_expire_if_stale / claim_pending_envelopes — the envelope is skipped by the sweep and not claimed (same as unparseable). - tools/browser_lightpanda.py::reap_orphaned_lightpanda — record unlinked, scan continues. - tools/write_approval.py::list_pending / get_pending — record skipped with the existing "unreadable pending record" warning / None. - tui_gateway/methods_session.py::_legacy_spawn_tree_entry / spawn_tree.load — scalar snapshot reads as empty / returns the existing 5000 error instead of violating the SpawnTreeLoadResult contract. - hermes_cli/local_runtime/binaries.py::manifest_verified — False. - plugins/platforms/a2a/protocol.py::load_conversation — non-dict lines are dropped, keeping the declared list[dict] return. - batch_runner.py::_load_dataset / _scan_completed_prompts_by_content / _combine_batch_files — line skipped and counted as filtered. - trajectory_compressor.py::process_entry_async — scalar entry passed through unchanged. Ported from the source hunks of PR #114241; its gateway/shutdown_flush.py drain_transcript_spool hunk is left to open PR #84785, and its recover_pending_to_db / cron / bot_live_delivery / bot_mode_dm hunks are already on this branch or on main. (cherry picked from commit d4b54568887e69b3ee3d363ebe4dcd657ccf64f9)
This commit is contained in:
@@ -2160,6 +2160,8 @@ def _legacy_spawn_tree_entry(p, session_dir_name: str) -> dict | None:
|
||||
raw = {}
|
||||
with contextlib.suppress(Exception):
|
||||
raw = json.loads(p.read_text(encoding="utf-8"))
|
||||
if not isinstance(raw, dict):
|
||||
raw = {}
|
||||
subagents = raw.get("subagents") or []
|
||||
return {"path": str(p), "session_id": raw.get("session_id") or session_dir_name,
|
||||
"finished_at": raw.get("finished_at") or stat.st_mtime, "started_at": raw.get("started_at"),
|
||||
@@ -2198,6 +2200,8 @@ def _(rid, params: dict) -> dict:
|
||||
payload = json.loads(resolved.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError) as exc:
|
||||
return _err(rid, 5000, f"spawn_tree.load failed: {exc}")
|
||||
if not isinstance(payload, dict):
|
||||
return _err(rid, 5000, "spawn_tree.load failed: snapshot is not a JSON object")
|
||||
return _ok(rid, payload)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user