databendlabs/databend · error

require 1 or 2 addresses in peer str

Error message

require 1 or 2 addresses in peer str: {}

What it means

Raised in build_nodes while parsing each entry of the --initial-cluster list. Each peer string must be '<NodeId>=<host>:<port>' with an optional obsolete second gRPC address; this error fires when the address part after '=' does not split into one or two host:port addresses (e.g. empty or extra commas).

Solutions

  1. Provide at most two comma-separated addresses per peer: '<id>=<addr1>[,<addr2>]'
  2. Split multi-node clusters into separate peer entries, one per node id
  3. Remove trailing commas that create empty address tokens

Example fix

// before
--initial-cluster 1=a:9191,b:9191,c:9191
// after
--initial-cluster 1=a:9191,b:9191 2=c:9191
Defensive patterns

Strategy: validation

Validate before calling

for peer in initial_cluster {
    if let Some(addrs) = peer.split('=').nth(1) {
        let n = addrs.split(',').filter(|s| !s.is_empty()).count();
        if !(1..=2).contains(&n) {
            eprintln!("peer {} must have 1 or 2 addresses", peer);
        }
    }
}

Try / catch

match build_nodes(initial_cluster) {
    Err(e) if e.to_string().contains("require 1 or 2 addresses") => eprintln!("one peer entry per node, max 2 addresses each"),
    other => other,
}

Prevention

When it happens

Trigger: --initial-cluster peer strings like '1=addr1,addr2,addr3' (3 addresses) or '1=' (empty address list).

Common situations: Listing the whole cluster on one peer line instead of one entry per node; trailing commas creating an empty address; misunderstanding that each node needs its own peer entry.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/9810ec4c49d34237. Report an issue: GitHub.

Appendix: source

Thrown at src/meta/control/src/import.rs:160

///
/// Raw config is: `<NodeId>=<raft-api-host>:<raft-api-port>[,...]`, e.g. `1=localhost:29103` or `1=localhost:29103,0.0.0.0:19191`
/// The second part is obsolete grpc api address and will be just ignored. Databend-meta loads Grpc address from config file when starting up.
fn build_nodes(initial_cluster: Vec<String>, id: u64) -> anyhow::Result<BTreeMap<NodeId, Node>> {
    eprintln!("Initialize Cluster: id={}, {:?}", id, initial_cluster);

    let mut nodes = BTreeMap::new();
    for peer in initial_cluster {
        eprintln!("peer:{}", peer);

        let id_addrs: Vec<&str> = peer.split('=').collect();
        if id_addrs.len() != 2 {
            return Err(anyhow::anyhow!("invalid peer str: {}", peer));
        }
        let id = u64::from_str(id_addrs[0])?;

        let addrs: Vec<&str> = id_addrs[1].split(',').collect();
        if addrs.len() > 2 || addrs.is_empty() {
            return Err(anyhow::anyhow!(
                "require 1 or 2 addresses in peer str: {}",
                peer
            ));
        }
        let url = Url::parse(&format!("http://{}", addrs[0]))?;
        let endpoint = match (url.host_str(), url.port()) {
            (Some(addr), Some(port)) => Endpoint::new(addr, port),
            _ => {
                return Err(anyhow::anyhow!("invalid peer raft addr: {}", addrs[0]));
            }
        };

        let node = Node::new(id, endpoint.clone());
        eprintln!("new cluster node:{}", node);

        nodes.insert(id, node);
    }

View on GitHub (pinned to 288d84d76e)