{"record":{"id":"78f06f4b27278f8c","repo":"risingwavelabs/risingwave","slug":"invalid-partition-index-number","errorCode":null,"errorMessage":"invalid partition index number","messagePattern":"invalid partition index number","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/connector/src/source/pulsar/topic.rs","lineNumber":67,"sourceCode":"\nimpl Topic {\n    pub fn is_partitioned_topic(&self) -> bool {\n        self.partition_index.is_none()\n    }\n\n    pub fn rest_path(&self) -> String {\n        format!(\n            \"{}/{}/{}/{}\",\n            self.domain,\n            self.tenant,\n            self.namespace,\n            encode(&self.topic)\n        )\n    }\n\n    pub fn sub_topic(&self, partition: i32) -> Result<Topic> {\n        if partition < 0 {\n            bail!(\"invalid partition index number\");\n        }\n\n        if self.topic.contains(PARTITIONED_TOPIC_SUFFIX) {\n            return Ok(self.clone());\n        }\n\n        Ok(Topic {\n            domain: self.domain.clone(),\n            tenant: self.tenant.clone(),\n            namespace: self.namespace.clone(),\n            topic: format!(\"{}{}{}\", self.topic, PARTITIONED_TOPIC_SUFFIX, partition),\n            partition_index: Some(partition),\n        })\n    }\n\n    pub fn topic_str_without_partition(&self) -> Result<String> {\n        if self.topic.contains(PARTITIONED_TOPIC_SUFFIX) {\n            let parts: Vec<&str> = self.topic.split(PARTITIONED_TOPIC_SUFFIX).collect();","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/source/pulsar/topic.rs#L49-L85","documentation":"Guard in Topic::sub_topic: rejects a negative partition index when constructing a partitioned Pulsar topic. Partition indexes must be non-negative; a negative value would produce an invalid topic name like `topic-partition-(-1)`, so the call fails instead.","triggerScenarios":"Calling sub_topic with a negative partition value, typically a -1 sentinel meaning 'no partition' passed where an actual partition index (>= 0) is required.","commonSituations":"Using -1 as a default/uninitialized partition value and passing it to sub_topic; off-by-one loop bounds producing a negative index; non-partitioned topics where partition info is absent.","solutions":["Pass a valid partition index >= 0; guard the call site for the non-partitioned case instead of using -1.","If the topic is non-partitioned, skip sub_topic entirely — topics already containing '-partition-' suffix are returned unchanged.","Fix upstream code that initializes partition numbers to -1."],"exampleFix":"// before\nlet topic = topic.sub_topic(partition)?; // partition = -1\n// after\nlet topic = if partition >= 0 { topic.sub_topic(partition)? } else { topic.clone() };","handlingStrategy":"type-guard","validationCode":"if partition < 0 {\n    return Err(\"partition index must be >= 0\".into());\n}","typeGuard":"fn valid_partition(p: i32) -> Option<u32> {\n    u32::try_from(p).ok()\n}","tryCatchPattern":"match topic.sub_topic(partition) {\n    Err(_) if partition < 0 => Ok(topic.clone()), // non-partitioned sentinel case\n    other => other,\n}","preventionTips":["Do not use -1 as a partition sentinel; branch on a partitioned/non-partitioned flag instead.","Use unsigned types for partition indexes where possible.","Check topic.partitions before computing per-partition topic names."],"tags":["pulsar","topic","validation","argument"],"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-14T16:17:12.679Z"}