Skip to content

fix(ov_dream): accept a message whose content is a plain string (#4221) - #4228

Merged
qin-ctx merged 2 commits into
volcengine:mainfrom
ntdat812:fix/ov-dream-string-content-4221
Aug 24, 2026
Merged

qin-ctx merged 2 commits into
volcengine:mainfrom
ntdat812:fix/ov-dream-string-content-4221

Conversation

@ntdat812

Copy link
Copy Markdown
Contributor

Fixes #4221.

Reproduced

One session file, two messages — a block list and a plain string:

AttributeError: 'str' object has no attribute 'get'

parse_messages() iterates message["content"] as a block list. A string iterates into characters, and "p".get("type") raises. The raise leaves parse_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-text blocks are still ignored.

Tests

examples/skills/ov_dream/tests/test_dream_message_content.py — 5 tests through the real parse_messages() with real session files on disk.

Against main with only scripts/dream.py reverted:

FAILED test_string_content_is_parsed_as_text
FAILED test_one_string_message_no_longer_drops_the_others
FAILED test_empty_and_unusable_bodies_are_skipped
FAILED test_message_text_helper_directly
4 failed, 1 passed

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_others is 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 check and ruff format --check clean.

Not changed

A single bad session still aborts the whole dream run, because sync_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.

ntdat812 and others added 2 commits August 24, 2026 10:06
…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
qin-ctx merged commit c66fee4 into volcengine:main Aug 24, 2026
@github-project-automation github-project-automation Bot moved this from Backlog to Done in OpenViking project Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug]: ov_dream dream.py raises AttributeError when a session message has string content

2 participants