zeroclaw-labs/zeroclaw · error

Recipient cannot be empty

Error message

Recipient cannot be empty

What it means

`recipient_to_jid` converts a recipient string into a WhatsApp JID for the WhatsApp Web channel; after trimming, an empty string bails immediately with this local validation error. It is raised from the send paths (`send`, typing indicators) when `message.recipient` is blank.

Source

Thrown at crates/zeroclaw-channels/src/whatsapp_web.rs:1180

                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({"error": format!("{}", err)})),
                "Failed to encode WhatsApp Web QR payload"
            );
            anyhow::Error::msg(format!("Failed to encode WhatsApp Web QR payload: {err}"))
        })?;

        Ok(qr
            .render::<qrcode::render::unicode::Dense1x2>()
            .quiet_zone(true)
            .build())
    }

    #[cfg(feature = "whatsapp-web")]
    fn recipient_to_jid(&self, recipient: &str) -> Result<wacore_binary::jid::Jid> {
        let trimmed = recipient.trim();
        if trimmed.is_empty() {
            anyhow::bail!("Recipient cannot be empty");
        }

        if trimmed.contains('@') {
            return trimmed.parse::<wacore_binary::jid::Jid>().map_err(|e| {
                ::zeroclaw_log::record!(
                    WARN,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                        .with_attrs(::serde_json::json!({
                            "trimmed": trimmed,
                            "error": format!("{}", e),
                        })),
                    "whatsapp_web: invalid JID"
                );
                anyhow::Error::msg(format!("Invalid WhatsApp JID `{trimmed}`: {e}"))
            });
        }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Require and validate a recipient before enqueueing the send.
  2. Default to a configured admin/test number when the target is missing, if that suits your flow.
  3. Fix the data source that produced the blank recipient.

Example fix

// before
channel.send(&SendMessage::new(body, "")).await?; // blank recipient -> bails

// after
let Some(to) = recipient else {
    anyhow::bail!("no recipient available for this reply");
};
channel.send(&SendMessage::new(body, &to)).await?;
Defensive patterns

Strategy: validation

Validate before calling

let to = recipient.trim();
anyhow::ensure!(!to.is_empty(), "recipient is required before sending");
channel.send(&SendMessage::new(text, to)).await

Type guard

fn non_empty_recipient(recipient: &str) -> bool {
    !recipient.trim().is_empty()
}

Prevention

When it happens

Trigger: Any send or typing call whose recipient is empty or only whitespace, e.g. `SendMessage::new(text, "")` or a recipient field that a template variable rendered to nothing.

Common situations: Upstream data missing a phone number (empty DB column, unset config target); replying to an inbound event that carried no sender address; a default recipient env var that was never set.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/307ac1b6a466b506. Report an issue: GitHub.