Skip to content

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

Description

Issue Origin

Observed or reproduced in a real environment

Bug Description

sync_session() in examples/skills/ov_dream/scripts/dream.py assumes every message's content field is a list[dict] of blocks. In real OpenClaw transcripts some messages store content as a plain string. Iterating a string yields single characters, and calling .get() on a character raises AttributeError, which aborts the entire sync so no session gets committed.

Measured distribution over 1609 messages from one real OpenClaw workspace:

content type count
list 1518
str 91
blocks inside lists (all dict) 2154

So roughly 5.7% of messages hit this path — on this machine it meant the skill could never complete a single sync until patched.

Steps to Reproduce

  1. Install the ov_dream skill from main branch into an OpenClaw workspace.
  2. Configure serverless mode (OPENVIKING_AUTH_MODE=serverless) with a valid API key.
  3. Ensure at least one session transcript contains a message whose content is a plain string rather than a block list (this happens naturally in real usage).
  4. Run python3 scripts/dream.py dream.
  5. The run aborts; no session is committed.

Expected Behavior

Messages with string content should be treated as plain text and synced normally. Sync should complete and commit all sessions.

Actual Behavior

The run aborts with AttributeError: 'str' object has no attribute 'get'. No session is committed, so the skill is completely non-functional on any workspace that contains at least one such message.

Minimal Reproducible Example

# any message shaped like this triggers it
msg = {"role": "user", "content": "plain string instead of block list"}

# dream.py, around line 289
blocks = msg.get("content", [])
text_parts = [
    block.get("text", "").strip()
    for block in blocks
    if block.get("type") == "text"
]
# -> AttributeError: 'str' object has no attribute 'get'


Suggested fix (three defenses: handle str, coerce non-list to empty, require each block to be a dict):


blocks = message.get("content", [])
if isinstance(blocks, str):
    content = blocks.strip()
else:
    if not isinstance(blocks, list):
        blocks = []
    text_parts = [
        block.get("text", "").strip()
        for block in blocks
        if isinstance(block, dict) and block.get("type") == "text"
    ]
    content = "\n".join(part for part in text_parts if part)


After applying this locally, a full sync completed: 10 sessions, all committed=True, 522 messages, rc=0, 9m14s.

Error Logs

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

Note on diagnosability: `main()` wraps everything in `except Exception: print(str(exc)); return 1`, which swallows the traceback. The user only sees that single line with no file or line number. I had to write a custom driver that does `import dream` and calls `sync_session()` directly with `traceback.print_exc()` in order to locate the failing line. Re-raising, or printing `traceback.format_exc()` behind a debug flag, would turn this into a 30-second fix.

OpenViking Version

examples/skills/ov_dream from main branch, fetched 2026-08-23 (raw.githubusercontent.com/volcengine/OpenViking/main/examples/skills/ov_dream/scripts/dream.py). The skill files carry no version string.

Python Version

3.12.3

Operating System

Linux

Model Backend

None

Additional Context

Environment: OpenClaw workspace, transcripts at ~/.openclaw/agents/main/sessions/*.jsonl, serverless auth mode against api.vikingdb.cn-beijing.volces.com.

Two unrelated minor observations while setting this up:

  1. The setup instructions ask the user to supply OPENVIKING_AGENT_ID, but grep -c AGENT_ID scripts/dream.py returns 0 — the script never reads it. Removing it from the docs would avoid confusion (I nearly went asking for a value that turned out to be unused).

  2. The docs suggest running the sync every 5 minutes. In practice each message costs one separate HTTP round-trip (measured 0.95-1.87s each), so a first full sync of 1609 messages takes roughly 27 minutes and a 5-minute schedule will pile up overlapping runs. Mentioning a mutex (e.g. flock -n) or a longer default interval in the docs would help. I settled on every 15 minutes with flock.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions