fix(ov_dream): accept a message whose content is a plain string (#4221) - #4228
Merged
qin-ctx merged 2 commits intoAug 24, 2026
Merged
Conversation
…engine#4221) parse_messages() assumed every OpenClaw message body was a block list. Iterating a plain string yields characters, so `"p".get("type")` raised AttributeError out of the parser — before any session was committed, which made one such message disable the skill for the whole workspace rather than skip that message. The reporter measured 91 string bodies in 1609 real messages. Flatten the body through a helper that treats a string as text and skips blocks that are not dicts.
qin-ctx
approved these changes
Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4221.
Reproduced
One session file, two messages — a block list and a plain string:
parse_messages()iteratesmessage["content"]as a block list. A string iterates into characters, and"p".get("type")raises. The raise leavesparse_messages()before anything is committed, so the failure is not "that message is skipped" — it is "no session in the workspace is ever synced". The reporter measured 91 string bodies in 1609 real messages (5.7%).The fix
The body is flattened through a small helper: a string is text, a list keeps the existing block handling, and blocks that are not dicts are skipped rather than raising the same way. Nothing else about the parse changes — whitespace-only and empty bodies are still dropped, and non-
textblocks are still ignored.Tests
examples/skills/ov_dream/tests/test_dream_message_content.py— 5 tests through the realparse_messages()with real session files on disk.Against
mainwith onlyscripts/dream.pyreverted:The one that passes on both is
test_block_lists_still_behave_the_same, which pins the behaviour that must not change.test_one_string_message_no_longer_drops_the_othersis the one that matters most: it asserts the messages after the string body still arrive, which is the difference between "one message lost" and "the run aborted".With the fix:
examples/skills/ov_dream/tests/— 15 passed (10 pre-existing + 5 new).ruff checkandruff format --checkclean.Not changed
A single bad session still aborts the whole
dreamrun, becausesync_active_session()has no per-session error boundary. That is a wider behavioural decision than this bug needs, so I left it alone — happy to follow up if you want one session's failure to stop being everyone's.