zeroclaw-labs/zeroclaw · error
WhatsApp interactive list requires at least one section
Error message
WhatsApp interactive list requires at least one section
What it means
`send_interactive_list` builds a WhatsApp interactive list message, and Meta's contract requires at least one section. The channel bails immediately when the `sections` slice is empty rather than sending a request that would fail server-side. This is a local precondition failure; no network call is made.
Source
Thrown at crates/zeroclaw-channels/src/whatsapp.rs:612
self.post_to_meta(&url, &body).await
}
/// Send an interactive list message (more than 3 options, up to 10
/// rows per section, up to 10 sections per Meta's limits). `sections`
/// each carry a section title + rows. Each row's `id` round-trips
/// through the inbound `interactive.list_reply.id` as `[choice]<id>`.
///
/// `button_text` is the button label that opens the list (Meta limits
/// to 20 chars).
pub async fn send_interactive_list(
&self,
recipient: &str,
body_text: &str,
button_text: &str,
sections: &[InteractiveListSection],
) -> anyhow::Result<()> {
if sections.is_empty() {
anyhow::bail!("WhatsApp interactive list requires at least one section");
}
if sections.len() > 10 {
anyhow::bail!(
"WhatsApp interactive list capped at 10 sections (got {})",
sections.len()
);
}
for s in sections {
if s.rows.len() > 10 {
anyhow::bail!(
"WhatsApp interactive list section '{}' capped at 10 rows (got {})",
s.title,
s.rows.len()
);
}
}
let url = format!(
"https://graph.facebook.com/v18.0/{}/messages",View on GitHub (pinned to 88bb9c8533)
Solutions
- Guard the empty case upstream: with no options, send the prompt as plain text via `send` (or no-op when the prompt is also empty).
- Debug why the sections-producing collection was empty — usually the input list was empty or fully filtered.
- Prefer `send_choice`, which already handles empty options by falling back to a plain-text send.
Example fix
// before
channel
.send_interactive_list(to, "Choose:", "Choose", §ions)
.await?; // sections is empty -> bails
// after
if sections.is_empty() {
channel.send(&SendMessage::new(prompt, to)).await?;
} else {
channel.send_interactive_list(to, "Choose:", "Choose", §ions).await?;
} Defensive patterns
Strategy: validation
Validate before calling
if sections.is_empty() {
anyhow::bail!("refusing to send an empty interactive list; check the option source");
}
channel.send_interactive_list(recipient, body, button_text, §ions).await Type guard
fn has_list_sections(sections: &[InteractiveListSection]) -> bool {
!sections.is_empty()
} Prevention
- Prefer `send_choice` for caller-supplied option lists; its empty-options path already degrades to plain text.
- Assert non-empty inputs where sections are generated (empty collection in, bail early with context).
- Property-test section builders so they never emit zero sections for non-empty input.
When it happens
Trigger: Calling `send_interactive_list(recipient, body_text, button_text, &[])` — typically sections produced by a `chunks`/`map`/filter chain over an empty or fully filtered-out option collection.
Common situations: Grouping options into sections algorithmically (e.g. `options.chunks(10)`) when `options` itself is empty; an upstream data source returning zero rows; scaffolding code passing a placeholder empty vec.
Related errors
- WhatsApp interactive list capped at 10 sections (got {})
- WhatsApp interactive list section '{}' capped at 10 rows (go
- WhatsApp send_choice: {} options exceeds list cap ({}); spli
- WhatsApp interactive buttons require 1..=3 options (got {});
- WhatsApp location marker must be `lat,lng[,name[,address]]`
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/160045afb81e0473.
Report an issue: GitHub.