{"record":{"id":"0d244743b985ed44","repo":"zeroclaw-labs/zeroclaw","slug":"discord-send-has-no-recipient-message-recipient-i","errorCode":null,"errorMessage":"discord send has no recipient: message.recipient is empty and no channel_ids are configured to fall back to","messagePattern":"discord send has no recipient: message\\.recipient is empty and no channel_ids are configured to fall back to","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/discord/mod.rs","lineNumber":1794,"sourceCode":"            if self.stream_mode == zeroclaw_config::schema::StreamMode::MultiMessage {\n                self.multi_message_delay_ms\n            } else {\n                500\n            };\n\n        let mut first_message_id: Option<String> = None;\n        let effective_recipient =\n            match effective_discord_recipient(&message.recipient, &self.channel_ids) {\n                Some(r) => r,\n                None => {\n                    ::zeroclaw_log::record!(\n                        WARN,\n                        ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)\n                            .with_outcome(::zeroclaw_log::EventOutcome::Unknown),\n                        \"discord send has no recipient: message.recipient is empty and no \\\n                     channel_ids are configured to fall back to\"\n                    );\n                    anyhow::bail!(\n                        \"discord send has no recipient: message.recipient is empty and no \\\n                     channel_ids are configured to fall back to\"\n                    );\n                }\n            };\n        for (i, chunk) in chunks.iter().enumerate() {\n            let message_id = if i == 0 && (!embeds.is_empty() || !component_action_rows.is_empty())\n            {\n                let payload = DiscordOutgoing {\n                    content: Some(chunk.clone()),\n                    embeds: embeds.clone(),\n                    components: component_action_rows.clone(),\n                    ..Default::default()\n                };\n                if local_files.is_empty() {\n                    send_discord_message_payload(\n                        &client,\n                        &self.bot_token,","sourceCodeStart":1776,"sourceCodeEnd":1812,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/discord/mod.rs#L1776-L1812","documentation":"send() resolves the delivery target via effective_discord_recipient: a non-empty message.recipient wins; empty falls back to the first configured channel_ids entry. When recipient is empty AND channel_ids is empty, there is nowhere to POST (the code would hit /channels//messages), so it logs a WARN and bails. Typical producer: system-generated messages such as escalation alerts that carry no per-message target on a channel never given default channel_ids.","triggerScenarios":"Escalation/alert SendMessage built with an empty recipient on a Discord channel configured only with a bot token; channel_ids lost in a config migration or split into per-alias configs; message routed to an alias whose channel has no channel_ids; gateway-driven sends relying on the fallback that was never configured.","commonSituations":"Enabling alerting before the bot was invited anywhere; splitting one Discord config into multiple aliases and forgetting channel_ids on the new one; tests constructing SendMessage with default values.","solutions":["Add channel_ids = [\"<channel snowflake>\"] to the Discord channel config so empty-recipient messages have a fallback","Set message.recipient to a concrete channel id when constructing the outgoing message","If the empty-recipient send is intentional nowhere-land, suppress it upstream instead of relying on this error","Verify the right alias — check the per-alias channel config the message actually routed to"],"exampleFix":"# before\n[channels.discord.default]\nbot_token = \"...\"\n\n# after — escalation alerts with empty recipients fall back to this channel\n[channels.discord.default]\nbot_token = \"...\"\nchannel_ids = [\"1234567890123456789\"]","handlingStrategy":"validation","validationCode":"// Mirror of effective_discord_recipient before calling send:\nlet target = if msg.recipient.is_empty() {\n    channel_ids.first().map(String::as_str)\n} else {\n    Some(msg.recipient.as_str())\n};\nif target.is_none() {\n    // nowhere to deliver — fix config or the message instead of sending\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always configure channel_ids as the default route on every Discord alias","Build alert messages with an explicit recipient whenever a target is known","Fail config validation at startup when a Discord alias has neither channel_ids nor a recipient source"],"tags":["discord","config","recipient","routing"],"backgroundTag":"missing-message-recipient","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}