{"record":{"id":"4591016cc809594d","repo":"risingwavelabs/risingwave","slug":"the-first-argument-vnode-count-must-be-in-range","errorCode":null,"errorMessage":"the first argument (vnode count) must be in range 1..={}","messagePattern":"the first argument \\(vnode count\\) must be in range 1\\.\\.=(.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/impl/src/scalar/vnode.rs","lineNumber":76,"sourceCode":"        }\n        .boxed()),\n    }\n}\n\n#[build_function(\"vnode_user(...) -> int2\")]\nfn build_user(_: DataType, children: Vec<BoxedExpression>) -> Result<BoxedExpression> {\n    let mut children = children.into_iter();\n\n    let vnode_count = children\n        .next()\n        .unwrap() // always exist, argument number enforced in binder\n        .eval_const() // required to be constant\n        .context(\"the first argument (vnode count) must be a constant\")?\n        .context(\"the first argument (vnode count) must not be NULL\")?\n        .into_int32(); // always int32, casted during type inference\n\n    if !(1i32..=VirtualNode::MAX_COUNT as i32).contains(&vnode_count) {\n        return Err(anyhow::anyhow!(\n            \"the first argument (vnode count) must be in range 1..={}\",\n            VirtualNode::MAX_COUNT\n        )\n        .into());\n    }\n\n    let children = children.collect_vec();\n    let all_indices = (0..children.len()).collect();\n    match try_into_sync_exprs(children) {\n        Ok(children) => Ok(VnodeExpression {\n            vnode_count: Some(vnode_count.try_into().unwrap()),\n            all_indices,\n            children,\n        }\n        .boxed()),\n        Err(children) => Ok(VnodeExpression {\n            vnode_count: Some(vnode_count.try_into().unwrap()),\n            all_indices,","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/impl/src/scalar/vnode.rs#L58-L94","documentation":"The `vnode(key1, key2, ..., vnode_count)` function requires its last constant argument to be an integer between 1 and `VirtualNode::MAX_COUNT` (32768). Values outside this range cannot form a valid vnode distribution, so `build_user` rejects them.","triggerScenarios":"Building a `vnode` expression where the constant vnode-count argument is 0, negative, or greater than 32768 (`VirtualNode::MAX_COUNT` as i32).","commonSituations":"Configuring distribution keys or hash bucketing with an invalid bucket count; typo like `vnode(k, 0)`; copying a vnode count from a wrong config (e.g. 65536).","solutions":["Pass a vnode count between 1 and 32768 inclusive","Check the value in the DDL/query that builds the vnode expression","If the count comes from a config variable, validate/clamp it before constructing the SQL"],"exampleFix":"-- before\nSELECT vnode(k, 0) FROM t;\n-- after\nSELECT vnode(k, 128) FROM t;","handlingStrategy":"validation","validationCode":"if (!Number.isInteger(count) || count < 1 || count > 32768) throw new Error(`vnode count must be in 1..=32768, got ${count}`);","typeGuard":null,"tryCatchPattern":"try { await rw.query(`SELECT vnode(k, ${count}) FROM t`) } catch (e) { if (String(e).includes('must be in range 1..=')) { /* clamp count and retry */ } else throw e; }","preventionTips":["Validate vnode counts from config before building SQL","Clamp bucket counts to 1..=32768","Write tests for boundary values 0, 1, 32768, 32769"],"tags":["sql","arguments","range"],"backgroundTag":"argument-out-of-range","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}