{"record":{"id":"33411a5db4a20e28","repo":"can1357/oh-my-pi","slug":"assistantmessageevent-content-must-be-a-string","errorCode":null,"errorMessage":"assistantMessageEvent.content must be a string","messagePattern":"assistantMessageEvent\\.content must be a string","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/omp-rpc/src/omp_rpc/protocol.py","lineNumber":410,"sourceCode":"        return AssistantToolCallDeltaEvent(\n            type=\"toolcall_delta\",\n            contentIndex=content_index,\n            delta=delta,\n            partial=partial,\n        )\n    if event_type in {\"text_end\", \"thinking_end\"}:\n        partial = _parse_assistant_message(\n            _clone_json_object(\n                payload.get(\"partial\"), field=\"assistantMessageEvent.partial\"\n            ),\n            field=\"assistantMessageEvent.partial\",\n        )\n        content_index = _optional_int(payload, \"contentIndex\")\n        content = _optional_str(payload, \"content\")\n        if content_index is None:\n            raise ValueError(\"assistantMessageEvent.contentIndex must be an integer\")\n        if content is None:\n            raise ValueError(\"assistantMessageEvent.content must be a string\")\n        if event_type == \"text_end\":\n            return AssistantTextEndEvent(\n                type=\"text_end\",\n                contentIndex=content_index,\n                content=content,\n                partial=partial,\n            )\n        return AssistantThinkingEndEvent(\n            type=\"thinking_end\",\n            contentIndex=content_index,\n            content=content,\n            partial=partial,\n        )\n    if event_type == \"toolcall_end\":\n        partial = _parse_assistant_message(\n            _clone_json_object(\n                payload.get(\"partial\"), field=\"assistantMessageEvent.partial\"\n            ),","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/omp-rpc/src/omp_rpc/protocol.py#L392-L428","documentation":"End events must carry the finalized block content as a string. The parser raises this error when the 'content' key is absent or is not a string, so AssistantTextEndEvent/AssistantThinkingEndEvent always expose the complete text.","triggerScenarios":"text_end/thinking_end notification missing 'content', or with content as an object/array/null; an emitter that only sends deltas and skips the final content accumulation.","commonSituations":"Custom bridges that emit end markers without accumulating full text; providers whose end event uses a differently named field; protocol version mismatch.","solutions":["Include the full accumulated content string in end events","Fix the emitter to accumulate deltas and populate content on end","If the full content is unavailable, accumulate text_delta fragments client-side and synthesize a valid event","Upgrade mismatched protocol implementations"],"exampleFix":"// before\n{\"type\": \"text_end\", \"contentIndex\": 0}\n// after\n{\"type\": \"text_end\", \"contentIndex\": 0, \"content\": \"full text\"}","handlingStrategy":"validation","validationCode":"if event.get(\"type\") in {\"text_end\", \"thinking_end\"} and not isinstance(event.get(\"content\"), str):\n    raise ValueError(\"end event missing string content\")","typeGuard":"def has_string_content(e: dict) -> bool:\n    return isinstance(e.get(\"content\"), str)","tryCatchPattern":"try:\n    event = parse_assistant_message_event(payload)\nexcept ValueError as e:\n    logger.warning(\"end event without content: %s\", e)\n    event = None  # optionally reconstruct from accumulated deltas","preventionTips":["Accumulate deltas server-side and include full content on end events","Never omit 'content'; use \"\" when empty","Round-trip test synthesized events through the parser"],"tags":["protocol","validation","streaming"],"backgroundTag":"missing-required-field","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}