{"record":{"id":"fe3a3425e10f83d4","repo":"iOfficeAI/OfficeCLI","slug":"invalid-outline-value-addrowoutline-expect","errorCode":null,"errorMessage":"Invalid 'outline' value: '{addRowOutline}'. Expected an integer 0-7 (outline/group level).","messagePattern":"Invalid 'outline' value: '(.+?)'\\. Expected an integer 0-7 \\(outline/group level\\)\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Add.Cells.cs","lineNumber":241,"sourceCode":"        // down. Gate only on \"inserting at a position\" (index set), NOT on the\n        // presence of cell data at/below — sheet-level structures (CF / merge /\n        // dataValidation) anchored on still-empty cells must shift too. Mirrors\n        // AddCol, which calls ShiftColumnsRight on every positional insert\n        // (CONSISTENCY(add-row-col-shift)). When nothing sits at/below rowIdx this\n        // is a harmless no-op.\n        // Validate all props BEFORE the structural shift (same atomicity rule\n        // as AddCol): a height/outline parse failure after ShiftRowsDown left\n        // the shift applied even though the add reported an error.\n        double? parsedRowHeight = null;\n        if (properties.TryGetValue(\"height\", out var addRowHeight) && !string.IsNullOrWhiteSpace(addRowHeight))\n            parsedRowHeight = ParseRowHeightPoints(addRowHeight);\n        byte? parsedRowOutline = null;\n        if (properties.TryGetValue(\"outline\", out var addRowOutline)\n            || properties.TryGetValue(\"outlinelevel\", out addRowOutline)\n            || properties.TryGetValue(\"group\", out addRowOutline))\n        {\n            if (!byte.TryParse(addRowOutline, out var addRowOutlineVal) || addRowOutlineVal > 7)\n                throw new ArgumentException($\"Invalid 'outline' value: '{addRowOutline}'. Expected an integer 0-7 (outline/group level).\");\n            parsedRowOutline = addRowOutlineVal;\n        }\n\n        bool needsShift = index.HasValue;\n        if (needsShift)\n            ShiftRowsDown(worksheet, rowIdx);\n\n        var newRow = new Row { RowIndex = (uint)rowIdx };\n\n        // CONSISTENCY(add-set-symmetry): accept height/hidden at creation\n        // time, mirroring SetRow semantics (ExcelHandler.Set.cs L3157-3164).\n        if (parsedRowHeight is { } rh)\n        {\n            newRow.Height = rh;\n            newRow.CustomHeight = true;\n        }\n        if (properties.TryGetValue(\"hidden\", out var addRowHidden))\n        {","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Add.Cells.cs#L223-L259","documentation":"Excel's outline/group level is stored as a byte 0-7 (the OOXML `outlineLevel` attribute). AddRow accepts the value via the `outline`, `outlinelevel`, or `group` property aliases. This guard parses it as a byte and rejects anything outside 0-7 before the structural ShiftRowsDown runs — validating before the shift (atomicity rule) prevents a half-applied row shift on a failed add.","triggerScenarios":"Add(\"/Sheet1\",\"row\",position,{[\"outline\"]=\"8\"}); or outlinelevel=\"-1\"; group=\"abc\" (fails byte.TryParse); group=\"9\" (>7).","commonSituations":"Treating the outline level as a 1-based count instead of a 0-based level; copy-pasting a depth value from a tree structure that exceeds 8 nesting levels; passing a numeric string with a sign or decimal.","solutions":["Use an integer 0-7 where 0 = no grouping and 7 = deepest nested level.","If your source data has >7 levels, collapse the extra levels into the 7 cap before adding the row.","Ensure the value is a plain integer string (no sign, no decimal) — it is parsed as an unsigned byte."],"exampleFix":"// before\nhandler.Add(\"/Sheet1\", \"row\", null, new() { [\"outline\"] = \"8\" });\n// after\nhandler.Add(\"/Sheet1\", \"row\", null, new() { [\"outline\"] = \"7\" });","handlingStrategy":"validation","validationCode":"if (!byte.TryParse(outlineStr, out var lvl) || lvl > 7)\n    throw new ArgumentException(\"outline must be 0-7\");\nprops[\"outline\"] = lvl.ToString();\nh.Add(sheet, \"row\", pos, props);","typeGuard":"static bool IsValidOutlineLevel(string s) => byte.TryParse(s, out var v) && v <= 7;","tryCatchPattern":"try { h.Add(sheet, \"row\", pos, props); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"Expected an integer 0-7\"))\n{ /* fix the outline value and retry or use 0 */ }","preventionTips":["Clamp tree depth to 7 before mapping to outline levels.","Pass plain unsigned integer strings (no sign, no decimal).","Remember outline level is 0-based (0 = no group)."],"tags":["excel","xlsx","row","outline","validation"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}