zeroclaw-labs/zeroclaw · error

WhatsApp send_choice: {} options exceeds list cap ({}); spli

Error message

WhatsApp send_choice: {} options exceeds list cap ({}); split the prompt or call send_interactive_list directly

What it means

`send_choice` renders more than 3 options as a WhatsApp interactive list, and WhatsApp lists cap at 10 sections x 10 rows = 100 options total. Passing more than 100 options bails before any send. Calling `send_interactive_list` directly (as the message suggests) does not raise the ceiling — the underlying Meta caps are the same — so splitting or trimming is the real fix.

Source

Thrown at crates/zeroclaw-channels/src/whatsapp.rs:891

        if options.len() >= 2 && options.len() <= 3 {
            let buttons: Vec<(String, String)> = options
                .iter()
                .map(|(id, label)| (id.clone(), label.clone()))
                .collect();
            return self
                .send_interactive_buttons(recipient, prompt, &buttons)
                .await;
        }
        if options.len() > 3 {
            // List messages: max 10 rows per section, max 10 sections per
            // message → up to 100 options. Chunk into sections of ≤10 rows
            // each so we don't blow past the per-section row cap. Section
            // titles mark the index range so list UIs render cleanly.
            const MAX_ROWS_PER_SECTION: usize = 10;
            const MAX_SECTIONS: usize = 10;
            let max_total = MAX_ROWS_PER_SECTION * MAX_SECTIONS;
            if options.len() > max_total {
                anyhow::bail!(
                    "WhatsApp send_choice: {} options exceeds list cap ({}); split the prompt or call send_interactive_list directly",
                    options.len(),
                    max_total
                );
            }
            let sections: Vec<InteractiveListSection> = options
                .chunks(MAX_ROWS_PER_SECTION)
                .enumerate()
                .map(|(chunk_idx, chunk)| {
                    let start = chunk_idx * MAX_ROWS_PER_SECTION + 1;
                    let end = start + chunk.len() - 1;
                    let title = if options.len() <= MAX_ROWS_PER_SECTION {
                        "Options".to_string()
                    } else {
                        format!("Options {start} to {end}")
                    };
                    InteractiveListSection {
                        title,

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Limit choices to the top 100 or fewer and add a 'more' option that pages through the remainder.
  2. Split into multiple prompts by category, each within the cap.
  3. For very large structured sets, send a text summary or a link instead of an interactive list.

Example fix

// before
channel.send_choice(to, "Pick a doc:", &all_150_options).await?; // > 100 -> bails

// after
let mut top: Vec<(String, String)> = all_150_options.iter().take(99).cloned().collect();
top.push(("more".into(), "Show more results…".into())); // page on 'more'
channel.send_choice(to, "Pick a doc:", &top).await?;
Defensive patterns

Strategy: validation

Validate before calling

const CHOICE_CAP: usize = 100; // WhatsApp list: 10 sections x 10 rows
if options.len() > CHOICE_CAP {
    let top: Vec<_> = options.iter().take(CHOICE_CAP - 1).cloned().collect();
    // add a paging option and handle it in your reply handler
    channel.send_choice(recipient, prompt, &with_more_option(top)).await
} else {
    channel.send_choice(recipient, prompt, &options).await
}

Type guard

fn within_choice_cap(options: &[(String, String)]) -> bool {
    options.len() <= 100
}

Prevention

When it happens

Trigger: `send_choice(recipient, prompt, &options)` with `options.len() > 100` — e.g. an LLM emitting an unbounded tool-option list, or a search/directory flow returning 150 entries.

Common situations: Unbounded LLM-generated menus; product catalogs passed wholesale as choices; assuming the channel will silently truncate (it does not — it fails loudly).

Related errors


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