{"record":{"id":"8b9ffafb61c90b68","repo":"risingwavelabs/risingwave","slug":"invalid-variant-path-original-path","errorCode":null,"errorMessage":"invalid variant path `{original_path}`","messagePattern":"invalid variant path `(.+?)`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/common/src/types/variant.rs","lineNumber":924,"sourceCode":"\nfn parse_path(path: &str) -> anyhow::Result<Vec<PathToken>> {\n    let original_path = path;\n    let path = path.strip_prefix('$').unwrap_or(path);\n    let mut chars = path.chars().peekable();\n    let mut tokens = vec![];\n    while let Some(ch) = chars.next() {\n        match ch {\n            '.' => {\n                let mut field = String::new();\n                while let Some(&c) = chars.peek() {\n                    if c == '.' || c == '[' {\n                        break;\n                    }\n                    field.push(c);\n                    chars.next();\n                }\n                if field.is_empty() {\n                    bail!(\"invalid variant path `{original_path}`\");\n                }\n                tokens.push(PathToken::Field(field));\n            }\n            '[' => {\n                if matches!(chars.peek(), Some('\\'') | Some('\"')) {\n                    let quote = chars.next().unwrap();\n                    let mut field = String::new();\n                    let mut closed = false;\n                    for c in chars.by_ref() {\n                        if c == quote {\n                            closed = true;\n                            break;\n                        }\n                        field.push(c);\n                    }\n                    if !closed || chars.next() != Some(']') {\n                        bail!(\"invalid variant path `{original_path}`\");\n                    }","sourceCodeStart":906,"sourceCodeEnd":942,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/common/src/types/variant.rs#L906-L942","documentation":"The variant path parser tokenizes strings like `.a.b` or `['x']` into PathToken::Field / PathToken::Index. Inside a bare identifier (after '.'), an empty field name is invalid; when the loop ends and `field` is empty, the whole original path is rejected with this error.","triggerScenarios":"Calling the variant path parser with paths like `a..b`, `a.` (trailing dot creates empty field), or a path that is just `.` / ends with a separator producing an empty field token.","commonSituations":"Dynamically built paths (string concatenation) that accidentally include empty segments, user-supplied JSON path expressions with typos.","solutions":["Remove empty segments from the path (no `..`, no trailing `.`)","Validate the path string before calling the parser (reject empty identifiers)","Quote fields that are empty or contain special characters, e.g. `a['']` only if truly intended"],"exampleFix":"// before\nlet path = \"a..b\"; // invalid\n// after\nlet path = \"a.b\";","handlingStrategy":"validation","validationCode":"fn path_has_no_empty_fields(path: &str) -> bool {\n    let body: &str = path.strip_prefix('.').unwrap_or(path);\n    !body.is_empty() && !body.contains(\"..\") && !body.ends_with('.')\n}","typeGuard":null,"tryCatchPattern":"match parse_variant_path(path) {\n    Ok(tokens) => tokens,\n    Err(e) if e.to_string().contains(\"invalid variant path\") => Vec::new(),\n    Err(e) => return Err(e),\n}","preventionTips":["Never concatenate path segments blindly; join with '.' only for non-empty fields","Validate user-supplied JSON paths with a regex before parsing","Strip trailing separators from dynamically built paths"],"tags":["rust","variant","path-parsing"],"backgroundTag":"invalid-argument-format","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"}