{"record":{"id":"45d2b503f8dc333e","repo":"iOfficeAI/OfficeCLI","slug":"invalid-selector","errorCode":"invalid_selector","errorMessage":"Malformed selector: unclosed bracket in \"{selector}\"","messagePattern":"Malformed selector: unclosed bracket in \"(.+?)\"","errorType":"exception","errorClass":"CliException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/AttributeFilter.cs","lineNumber":105,"sourceCode":"        return blocks;\n    }\n\n    // Regex: numeric positional index [N] only (used for reverse-doc-order keys).\n    private static readonly Regex BracketIndexRegex = new(\n        @\"\\[(\\d+)\\]\",\n        RegexOptions.Compiled);\n\n    /// <summary>\n    /// Parse all [key op value] conditions from a selector string.\n    /// Throws CliException for malformed selectors.\n    /// </summary>\n    public static List<Condition> Parse(string selector)\n    {\n        // Check for unclosed brackets\n        var openCount = selector.Count(c => c == '[');\n        var closeCount = selector.Count(c => c == ']');\n        if (openCount != closeCount)\n            throw new CliException($\"Malformed selector: unclosed bracket in \\\"{selector}\\\"\")\n            {\n                Code = \"invalid_selector\",\n                Suggestion = \"Ensure every '[' has a matching ']'. Example: paragraph[style=Heading 1]\"\n            };\n\n        var conditions = new List<Condition>();\n        var matchedSpans = new HashSet<(int Start, int End)>();\n\n        foreach (Match m in AttrRegex.Matches(selector))\n        {\n            var key = m.Groups[1].Value;\n            var opStr = m.Groups[2].Value.Replace(\"\\\\\", \"\");\n            var rawVal = m.Groups[3].Value;\n            // CONSISTENCY(find-regex): preserve quotes when the value is the\n            // `r\"...\"` / `r'...'` regex form so MatchOne can detect it. Trim\n            // would otherwise eat the surrounding quote that marks the prefix.\n            var isRegexForm = rawVal.Length >= 3 && rawVal[0] == 'r'\n                && (rawVal[1] == '\"' || rawVal[1] == '\\'');","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/AttributeFilter.cs#L87-L123","documentation":"Thrown by AttributeFilter.Parse when the number of '[' characters in the selector does not equal the number of ']' characters. Attribute filters live inside brackets ([key=value], [key]), so an unbalanced bracket cannot form a valid filter. This is the simple (flat) parser used for single-condition selectors. It is a CliException with Code=invalid_selector.","triggerScenarios":"A selector like 'shape[fill=#FF0000' (missing closing bracket), or 'shape[fill=#FF0000][size' (second bracket unclosed). A path copied from output where a trailing ']' was trimmed.","commonSituations":"A user edits a working selector and deletes a ']'. An agent truncates a long path string. A value containing a literal ']' that the simple counter misreads (use the boolean-expression parser / quote the value instead).","solutions":["Count brackets and add the missing ']' so every '[' has a matching ']'.","If a value contains a literal bracket, wrap it in quotes (\"...\") or use the r\"...\" regex form so the quote-aware parser treats it as text.","Prefer the boolean-expression syntax (e.g. cell[a and b]) which is bracket-aware."],"exampleFix":"# before\nquery 'shape[fill=#FF0000'\n# after\nquery 'shape[fill=#FF0000]'","handlingStrategy":"validation","validationCode":"static void EnsureBalancedBrackets(string selector)\n{\n    var open = selector.Count(c => c == '[');\n    var close = selector.Count(c => c == ']');\n    if (open != close)\n        throw new ArgumentException($\"Selector has {open} '[' and {close} ']'; they must balance.\");\n}","typeGuard":"static bool HasBalancedBrackets(string s) =>\n    s.Count(c => c == '[') == s.Count(c => c == ']');","tryCatchPattern":"try { conditions = AttributeFilter.Parse(selector); }\ncatch (CliException ex) when (ex.Code == \"invalid_selector\" && ex.Message.Contains(\"unclosed bracket\"))\n{ /* balance the selector and retry */ }","preventionTips":["Always pair every '[' with a ']' when editing selectors.","If a value contains a bracket, quote it so the quote-aware parser treats it literally.","Validate bracket balance before sending a selector to the API."],"tags":["selector","attribute-filter","invalid-selector","parse","bracket"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}