{"record":{"id":"2e71d8541e6590a6","repo":"iOfficeAI/OfficeCLI","slug":"malformed-path-segment-part-bracket-is-no","errorCode":null,"errorMessage":"Malformed path segment '{part}'. Bracket '[' is not closed. Expected format: name[index] or name[@attr=value].","messagePattern":"Malformed path segment '(.+?)'\\. Bracket '\\[' is not closed\\. Expected format: name\\[index\\] or name\\[@attr=value\\]\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/GenericXmlQuery.cs","lineNumber":251,"sourceCode":"    /// <summary>\n    /// Parse a path string like \"a/b[1]/c[2]\" into segments of (Name, Index).\n    /// Index is 1-based. If no index specified, Index is null.\n    /// </summary>\n    public static List<(string Name, int? Index)> ParsePathSegments(string path)\n    {\n        var segments = new List<(string Name, int? Index)>();\n        foreach (var part in path.Trim('/').Split('/'))\n        {\n            if (string.IsNullOrEmpty(part)) continue;\n            var bracketIdx = part.IndexOf('[');\n            if (bracketIdx >= 0)\n            {\n                // BUG-R36-01 fix: when ']' is missing (e.g. \"slide[\") the expression\n                // part[(bracketIdx+1)..^1] produces a negative-length range crash.\n                // Detect and reject unclosed brackets with a clean ArgumentException.\n                var closingIdx = part.IndexOf(']', bracketIdx + 1);\n                if (closingIdx < 0)\n                    throw new ArgumentException($\"Malformed path segment '{part}'. Bracket '[' is not closed. Expected format: name[index] or name[@attr=value].\");\n                var name = PathAliases.Resolve(part[..bracketIdx]);\n                var indexStr = part[(bracketIdx + 1)..^1];\n                if (!int.TryParse(indexStr, out var idx))\n                    // A predicate in the index slot (row[Score>0], row[not(V)])\n                    // means the caller reached the single-node path navigator\n                    // with a FILTER. get is one-node-by-path by contract;\n                    // point at the verbs that run the selector engine.\n                    throw new ArgumentException(AttributeFilter.IsContentFilterPath($\"[{indexStr}]\")\n                        ? $\"'{part}' is a predicate, but this verb navigates by position and expects a numeric index (e.g. {part[..part.IndexOf('[')]}[2]). Predicates work on 'query' (read) and 'set'/'remove' (mutate matched elements).\"\n                        : $\"Invalid path index '{indexStr}' in segment '{part}'. Expected a numeric index.\");\n                if (idx < 1)\n                    throw new ArgumentException($\"Invalid path index '{idx}' in segment '{part}'. Index must be >= 1.\");\n                segments.Add((name, idx));\n            }\n            else\n            {\n                segments.Add((PathAliases.Resolve(part), null));\n            }","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/GenericXmlQuery.cs#L233-L269","documentation":"Thrown by the GenericXmlQuery path parser when a path segment contains a '[' but no matching ']'. This guard (BUG-R36-01 fix) prevents a negative-length range crash that previously occurred when slicing part[(bracketIdx+1)..^1] on an unclosed bracket like 'slide['.","triggerScenarios":"Calling a get/query path navigator with a segment such as 'slide[' or 'row[Score>0' — a '[' with no following ']'. The parser looks for ']' after the '[' and throws if none is found.","commonSituations":"Truncated path string; a predicate missing its closing bracket; user input that dropped the ']'; building paths by string concatenation with an off-by-one.","solutions":["Close the bracket: use name[index] or name[@attr=value].","Validate the path string has balanced '[' and ']' before calling the navigator.","If you intended a predicate filter, ensure it is fully closed and use the correct verb (query/set/remove)."],"exampleFix":"// before\nGet(xml, \"slides/slide[1\"); // missing ]\n// after\nGet(xml, \"slides/slide[1]\");","handlingStrategy":"validation","validationCode":"static bool BracketsClosed(string segment)\n{\n    int b = segment.IndexOf('[');\n    return b < 0 || segment.IndexOf(']', b + 1) >= 0;\n}","typeGuard":null,"tryCatchPattern":"try { Get(xml, path); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"Bracket '[' is not closed\"))\n{ /* add the missing ']' */ }","preventionTips":["Validate path segments have balanced brackets before navigating.","Build paths with a small helper that always closes brackets."],"tags":["xml","path","validation","xpath-like"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}