{"record":{"id":"968fb3728ce233d7","repo":"HKUDS/Vibe-Trading","slug":"feishu-message-content-must-not-be-empty","errorCode":null,"errorMessage":"Feishu message content must not be empty","messagePattern":"Feishu message content must not be empty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"agent/src/channels/feishu.py","lineNumber":2209,"sourceCode":"\n        except Exception:\n            self.logger.exception(\"Error sending message\")\n            raise\n\n    async def send_with_receipt(self, msg: OutboundMessage) -> DeliveryReceipt:\n        \"\"\"Send a non-reply message and retain Feishu's provider message id.\n\n        Scheduled briefings use this path. Rich reply/media sends keep the\n        generic adapter contract because Feishu's reply and upload APIs do not\n        expose one uniform receipt shape.\n        \"\"\"\n        if msg.media or msg.reply_to or msg.metadata:\n            return await super().send_with_receipt(msg)\n        if not self._client:\n            raise RuntimeError(\"Feishu client is not initialized\")\n        content = (msg.content or \"\").strip()\n        if not content:\n            raise ValueError(\"Feishu message content must not be empty\")\n\n        receive_id_type = \"chat_id\" if msg.chat_id.startswith(\"oc_\") else \"open_id\"\n        loop = asyncio.get_running_loop()\n        payloads: list[tuple[str, str]] = []\n        fmt = self._detect_msg_format(content)\n        if fmt == \"text\":\n            payloads.append((\"text\", json.dumps({\"text\": content}, ensure_ascii=False)))\n        elif fmt == \"post\":\n            payloads.append((\"post\", self._markdown_to_post(content)))\n        else:\n            elements = self._build_card_elements(content)\n            for chunk in self._split_elements_by_table_limit(elements):\n                payloads.append(\n                    (\n                        \"interactive\",\n                        json.dumps(\n                            {\"config\": {\"wide_screen_mode\": True}, \"elements\": chunk},\n                            ensure_ascii=False,","sourceCodeStart":2191,"sourceCodeEnd":2227,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/channels/feishu.py#L2191-L2227","documentation":"Raised by FeishuChannel.send_with_receipt when sending a plain text message (no media, reply, or metadata) whose content strips to empty. It is a client-side validation before any HTTP call, because the Feishu im/v1/messages API rejects empty text payloads. Uses ValueError rather than RuntimeError since it's caller input, not provider state.","triggerScenarios":"Calling send_with_receipt with an OutboundMessage whose content is '', None, or only whitespace, while msg.media, msg.reply_to, and msg.metadata are all falsy (those routes delegate to the base channel instead).","commonSituations":"Upstream LLM/agent produced an empty completion, templating rendered an empty string, whitespace-only content after trimming user input, or a bug where content was assigned to the wrong field.","solutions":["Check/trim msg.content before sending and skip or substitute a placeholder when empty","Fix the upstream producer (prompt/LLM output) so it never yields empty content","If you intended to send media or a reply, populate msg.media or msg.reply_to so the base channel path is used"],"exampleFix":"// before\nawait channel.send_with_receipt(OutboundMessage(chat_id=chat, content=\"   \"))\n\n// after\ntext = (content or \"\").strip()\nif not text:\n    text = \"(empty response)\"\nawait channel.send_with_receipt(OutboundMessage(chat_id=chat, content=text))","handlingStrategy":"validation","validationCode":"content = (msg.content or \"\").strip()\nif not content and not (msg.media or msg.reply_to):\n    skip_or_placeholder = True  # don't call send_with_receipt as-is","typeGuard":"def has_sendable_text(msg: OutboundMessage) -> bool:\n    return bool((msg.content or \"\").strip()) or bool(msg.media or msg.reply_to or msg.metadata)","tryCatchPattern":"null","preventionTips":["Normalize/trim LLM output before constructing OutboundMessage","Add an empty-output guard in the agent reply pipeline","Skip sending rather than letting ValueError abort the whole handler"],"tags":["feishu","validation","empty-message","send"],"backgroundTag":"empty-message-validation","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}