iOfficeAI/OfficeCLI · error · CliException

not_found

not_found

Error message

row[col op val] found no usable table on {scope}: a header '{h.name}' exists at {h.sheet}!{h.cellRef}, but the surrounding cells are not a recognizable table. Auto-detection needs at least 2 adjacent non-empty header columns and at least 1 data row; a blank-header column in the middle or a header-only block breaks it. Create the table explicitly (add <file> /{h.sheet} --type table --prop ref=<A1:D10>), or address cells directly.

What it means

Thrown by QueryRowsByColumnPredicate when no recognizable table was found in scope, but the predicate's column name DOES exist as a bare cell. This means the data is not structured as a detectable table: auto-detection requires at least 2 adjacent non-empty header columns and at least 1 data row. A blank-header gap column or a header-only block breaks detection.

Source

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

        }

        var candidates = listObjCands.Count > 0 ? listObjCands : detectedCands;

        if (candidates.Count == 0)
        {
            var cols = string.Join(", ", colConds.Select(c => $"'{StripColPrefix(c.Key)}'"));
            var scope = sheetFilter == null ? "any sheet" : $"sheet '{sheetFilter}'";
            // If NO table structure exists in scope at all, but the column NAME
            // literally exists as a cell, the honest cause is "not a recognizable
            // table" (a blank-header gap column, or a header-only block with no
            // data rows) — point at explicit table creation. Guard on an empty
            // scopeTables: when a real table WAS detected but simply doesn't own
            // one of a compound predicate's columns (`row[Region=X and Bonus>1]`
            // where Bonus is absent), fall through to BuildNoColumnException so
            // the error names the missing column and lists the available ones,
            // instead of wrongly blaming a valid column's surrounding cells.
            if (scopeTables.Count == 0 && FindHeaderLikeCell(sheetFilter, colConds) is {} h)
                throw new Core.CliException(
                    $"row[col op val] found no usable table on {scope}: a header '{h.name}' exists at " +
                    $"{h.sheet}!{h.cellRef}, but the surrounding cells are not a recognizable table. " +
                    "Auto-detection needs at least 2 adjacent non-empty header columns and at least 1 data " +
                    "row; a blank-header column in the middle or a header-only block breaks it. Create the " +
                    $"table explicitly (add <file> /{h.sheet} --type table --prop ref=<A1:D10>), or address cells directly.")
                {
                    Code = "not_found",
                    Suggestion = $"add <file> /{h.sheet} --type table --prop ref=<range covering the header and data>",
                };
            throw BuildNoColumnException(
                $"row[col op val] found no table on {scope} with column(s) {cols}. " +
                "Column predicates resolve header names (or column letters) against a ListObject or a detected (header-row) table.",
                scopeTables, colConds);
        }
        if (candidates.Count > 1)
        {
            var where = string.Join(", ", candidates.Select(c => $"{c.sheetName}!{c.label}"));
            var sheets = candidates.Select(c => c.sheetName).Distinct().ToList();

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Create the table explicitly so column names are stored metadata: add <file> /SheetName --type table --prop ref=<A1:D10> covering the header and data rows.
  2. If the layout is correct but detection is being defeated by a blank header, fill the blank header or remove the gap column.
  3. If you only need cells, address them directly (e.g. /Sheet1/B2) instead of using row[...].

Example fix

// before (header 'Region' sits alone or next to a blank cell)
get /Sheet1/row[Region='West']

// after (declare the table range explicitly)
add /file.xlsx /Sheet1 --type table --prop ref=A1:D100
get /Sheet1/row[Region='West']
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the target range is a real ListObject before relying on row[...].
bool HasTableCovering(ExcelHandler excel, string sheet, string rangeRef)
    => excel.Get($"/{sheet}").Children.Any(c => c.Type == "table" && (c.Format.TryGetValue("ref", out var r) && r?.ToString() == rangeRef));

Try / catch

try { var rows = excel.QueryRowsByColumnPredicate(sheet, expr); }
catch (Core.CliException ex) when (ex.Code == "not_found" && ex.Message.Contains("not a recognizable table"))
{ /* prompt user to add --type table with an explicit ref */ }

Prevention

When it happens

Trigger: Running row[Region=X] against a sheet where 'Region' is a single header cell with no adjacent populated headers, or where a blank column splits the header row, or where headers exist but there are no data rows beneath them.

Common situations: Importing a CSV whose first column header is blank; pasting a header-only block; a header row interrupted by a merged/empty cell; data laid out as a key/value block instead of a tabular table.

Related errors


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