{"record":{"id":"6d89dfb4b2c6f0db","repo":"iOfficeAI/OfficeCLI","slug":"table-ref-rangeref-has-1-row-tables-must-have","errorCode":null,"errorMessage":"table ref '{rangeRef}' has 1 row; tables must have at least 2 rows (header + 1 data row). Pass headerRow=false for a data-only single-row table.","messagePattern":"table ref '(.+?)' has 1 row; tables must have at least 2 rows \\(header \\+ 1 data row\\)\\. Pass headerRow=false for a data-only single-row table\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Add.Tables.cs","lineNumber":1216,"sourceCode":"                    if (!anyNonEmpty) break;\n                    endRow = probeRow;\n                    probeRow++;\n                }\n                rangeRef = $\"{startCol}{startRow}:{endCol}{endRow}\";\n            }\n        }\n\n        // i103: when headerRow=true (the default) the table ref must cover\n        // at least 2 rows — header plus one data row. A header-only ref\n        // (e.g. A1:C1) produces an <autoFilter> that Excel rejects with\n        // \"Removed Feature: AutoFilter from /xl/tables/tableN.xml part\",\n        // which cascades to drop the whole table on file open. Reject up\n        // front with a clear message instead of letting Excel silently\n        // strip the table. headerRow=false is allowed to be a single\n        // (data-only) row.\n        if (hasHeader && startRow == endRow)\n        {\n            throw new ArgumentException(\n                $\"table ref '{rangeRef}' has 1 row; tables must have at least 2 rows \" +\n                \"(header + 1 data row). Pass headerRow=false for a data-only single-row table.\");\n        }\n\n        // CONSISTENCY(table-totalrow): a:totalsRowShown MUST point at a row\n        // OUTSIDE the data area. Previously we reused endRow as the totals\n        // row, which overwrote whatever data lived on that last row. Expand\n        // the ref by one row so the totals row is appended below the data\n        // instead of stamping over it.\n        if (hasTotalRow)\n        {\n            endRow += 1;\n            rangeRef = $\"{startCol}{startRow}:{endCol}{endRow}\";\n        }\n\n        string[] colNames;\n        if (properties.TryGetValue(\"columns\", out var tblColsStr))\n        {","sourceCodeStart":1198,"sourceCodeEnd":1234,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Add.Tables.cs#L1198-L1234","documentation":"Thrown by AddTable's i103 guard when headerRow is true (the default) and the table ref covers exactly one row. A header-only ref (e.g. A1:C1) produces an <autoFilter> that Excel rejects with 'Removed Feature: AutoFilter', which cascades and drops the whole table on open. The handler requires at least a header plus one data row unless headerRow=false is set for a data-only single-row table.","triggerScenarios":"Calling Add('/Sheet1/table', ...) with ref=A1:C1 (one row) and no headerRow=false, i.e. relying on the default headerRow=true.","commonSituations":"Building a table from a header-only template, auto-sizing the ref to the used header row, or intending a single-row data table.","solutions":["Extend the ref to at least two rows (header + data), e.g. ref=A1:C2.","If a single data-only row is intended, pass headerRow=false.","Verify startRow equals endRow is the actual condition; any 2+ row ref passes."],"exampleFix":"// before\nadd /Sheet1/table --prop ref=A1:C1\n// after (header + data)\nadd /Sheet1/table --prop ref=A1:C2\n// or data-only\nadd /Sheet1/table --prop ref=A1:C1 --prop headerRow=false","handlingStrategy":"validation","validationCode":"// Validate a table ref has >=2 rows when headerRow is true (default).\nstatic bool TryValidateHeaderRef(string rangeRef, bool headerRow, out string error)\n{\n    error = null;\n    if (!headerRow) return true;\n    var (startRow, endRow) = ParseRows(rangeRef); // pseudo: extract numeric rows\n    if (startRow == endRow)\n    {\n        error = $\"table ref '{rangeRef}' has 1 row; pass headerRow=false for data-only.\";\n        return false;\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try { handler.Add(parentPath, \"table\", null, props); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"tables must have at least 2 rows\"))\n{ /* extend ref to 2+ rows or set headerRow=false, then retry */ }","preventionTips":["When templating from a header row, extend the ref to include at least one data row.","Set headerRow=false explicitly for data-only single-row tables.","Validate startRow != endRow in any builder that defaults headerRow=true."],"tags":["excel","table","validation","header-row","corruption-prevention"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}