iOfficeAI/OfficeCLI · error · Core.CliException

invalid_selector

invalid_selector

Error message

'{selector}' is not a valid selector: a predicate must be inside brackets, e.g. row[col.2024>150] to filter table rows by a column, or cell[value>150] to filter cells.

What it means

Thrown (code invalid_selector) when a query selector contains a comparison operator (>, <, =, etc.) OUTSIDE any bracket, e.g. 'col.2024>150' or 'Dept=IT' written bare. HasTopLevelComparison detects this; without the guard the selector would match no element type and silently return an empty list with exit 0 — a 'no rows' result a user mistakes for real data. The handler fails loud and shows the correct bracketed form.

Source

Thrown at src/officecli/Handlers/Excel/ExcelHandler.Query.cs:1136

        // other Query branches. Single-cell refs return a one-element list.
        var nativeCellRef = Regex.Match(selector, @"^([^/!]+)!([A-Z]+\d+(:[A-Z]+\d+)?)$", RegexOptions.IgnoreCase);
        if (nativeCellRef.Success)
        {
            // 'My Data (2024)'!A1 — Excel requires quoting names with spaces;
            // strip the quotes so the DOM path resolves the real sheet.
            var node = Get($"/{UnquoteSheetName(nativeCellRef.Groups[1].Value)}/{nativeCellRef.Groups[2].Value}");
            if (node.Type == "range" && node.Children.Count > 0)
                return node.Children;
            return [node];
        }

        // A comparison operator OUTSIDE any bracket means the predicate was
        // written without its brackets (`col.2024>150`, `foo>1`, `Dept=IT`).
        // Such a selector matches no element type and would otherwise return an
        // empty list with exit 0 — a silent "no rows" that a data user reads as a
        // real result. Fail loud with the bracketed form instead.
        if (HasTopLevelComparison(selector))
            throw new Core.CliException(
                $"'{selector}' is not a valid selector: a predicate must be inside brackets, " +
                $"e.g. row[col.2024>150] to filter table rows by a column, or cell[value>150] to filter cells.")
                { Code = "invalid_selector" };

        // CONSISTENCY(excel-sheet-separator-warn): Detect the PPT-style `>`
        // separator form (e.g. `Sheet1>ole`) that users familiar with the
        // PowerPoint query grammar may try against Excel. Excel uses `!`
        // (Sheet1!cell[...]) — the legacy spreadsheet separator — so a `>`
        // in the sheet-prefix slot will silently fall through to generic
        // XML and return an empty result. We emit a single stderr warning
        // pointing to the correct `!` form, then let the normal flow run.
        // Only fire when the prefix looks like a sheet name (no `/`) and
        // the suffix is a known Excel element type we would have handled.
        {
            var pptStyle = Regex.Match(selector, @"^([^/!>]+)>(\w+)");
            if (pptStyle.Success)
            {
                var suffixType = pptStyle.Groups[2].Value.ToLowerInvariant();

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Wrap the predicate in square brackets: row[col.2024>150] to filter table rows, or cell[value>150] to filter cells.
  2. When constructing selectors programmatically, always template the bracket form: f"row[col.{col}{op}{val}]".
  3. If you meant a path, not a filter, remove the comparison operator entirely.

Example fix

// before
get row col.2024>150
// after
get row[col.2024>150]
Defensive patterns

Strategy: validation

Validate before calling

import re
def brackets_present_around_predicate(selector):
    # a comparison operator must sit inside [...]
    outside = re.sub(r'\[[^\]]*\]', '', selector)  # strip bracketed groups
    return not re.search(r'[<>!=]=|[<>]', outside)

selector = 'col.2024>150'
assert brackets_present_around_predicate(selector), \
    'predicate must be inside brackets, e.g. row[col.2024>150]'

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Query/remove with a predicate whose brackets were omitted: 'row col.2024>150' or a selector string like 'col.2024>150' instead of 'row[col.2024>150]'. Any top-level comparison operator triggers it.

Common situations: Forgetting the square brackets around a filter expression; building selectors by string concatenation that drops the brackets; migrating from a grammar that used parens or no delimiters.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/961e1e83645335ba. Report an issue: GitHub.