Skip to content

Skip non-object rows when decoding session message JSON - #354

Merged
tylerreckart merged 9 commits into
mainfrom
fix/decode-messages-skip-non-objects
Sep 21, 2026
Merged

tylerreckart merged 9 commits into
mainfrom
fix/decode-messages-skip-non-objects

Conversation

@cursor

@cursor cursor Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Problem

decode_messages_json (TUI session / conversation-store restore via Orchestrator::load_session_json) only skipped null shared_ptrs in the message array. JSON null, strings, numbers, booleans, and nested arrays are stored as live JsonValues, so they were decoded as Message rows with empty role / content.

Those empty-role rows go back into the model view on the next turn. Providers reject unknown roles, and compaction / replay treat them as real history. tool_trace_from_json already skipped non-object entries; the message loop did not.

Corrupt or mixed-shape session JSON ([null, {"role":"user","content":"hi"}]) restored an empty user turn plus the real one. An all-non-object array restored empty-role rows instead of an empty history.

Fix

Skip any array element that is not a JSON object. Valid object rows, including those missing optional thinking / tool_trace, are unchanged. A wholly unreadable blob still fail-closes to {} via the existing parse catch.

Tests

unit_message_codec 3/3 locally (13 assertions) + ASan/UBSan. Covers mixed null/string/number/bool/array elements keeping valid objects, all-non-object arrays staying empty, and object round-trip.

Independently mergeable against main (0456350). Code hunks (src/message_codec.cpp, new tests/test_message_codec.cpp + CMakeLists.txt target after unit_message_replay_roundtrip) do not overlap #321–#353. git merge-tree --write-tree vs those siblings is CLEAN, including CHANGELOG [Unreleased] this run.

Open in Web View Automation 

Note

Low Risk
Narrow decode guard on session JSON restore; valid object messages unchanged, with new unit tests covering edge cases.

Overview
Session restore no longer turns junk entries in stored message JSON into fake chat turns. decode_messages_json now ignores any array element that is not a JSON object (null, strings, numbers, booleans, nested arrays, sparse null slots), aligned with how tool_trace_from_json already filters trace rows.

That stops TUI/conversation restore from rehydrating empty-role Message rows that providers reject and that compaction/replay treat as real history when session files are corrupt or mixed-shape (e.g. [null, {"role":"user",…}]).

Adds a focused unit_message_codec doctest target (mixed skip, all-invalid → empty vector, encode/decode round-trip) and documents the fix under [Unreleased] in the changelog.

Reviewed by Cursor Bugbot for commit 628eb0b. Bugbot is set up for automated code reviews on this repo. Configure here.

decode_messages_json only skipped null shared_ptrs, so JSON null,
strings, numbers, and arrays in a session history became empty-role
Message rows on TUI restore. Skip any element that is not a JSON
object, matching tool_trace_from_json.

Co-authored-by: Tyler Reckart <tylerreckart@users.noreply.github.com>
@tylerreckart
tylerreckart marked this pull request as ready for review September 21, 2026 12:58
@tylerreckart
tylerreckart enabled auto-merge (squash) September 21, 2026 12:58
@tylerreckart
tylerreckart merged commit 8448b42 into main Sep 21, 2026
2 of 6 checks passed
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.

3 participants