{"record":{"id":"4117f1e14da2df25","repo":"iOfficeAI/OfficeCLI","slug":"cannot-store-cell-cellvalue-text-as-number-v","errorCode":null,"errorMessage":"Cannot store '{cell.CellValue?.Text}' as number; value must be a finite numeric literal. Use type=string to keep the literal text.","messagePattern":"Cannot store '(.+?)' as number; value must be a finite numeric literal\\. Use type=string to keep the literal text\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Add.Cells.cs","lineNumber":723,"sourceCode":"                        // mirroring the type=date guard.\n                        throw new ArgumentException(\n                            $\"Cannot store '{cell.CellValue?.Text}' as boolean; value must be true/false, yes/no, or 1/0. \" +\n                            \"Use type=string to keep the literal text.\");\n                }\n                // A type=number cell stores its value in <v> with no t=\n                // attribute, so a non-numeric value produces spec-invalid\n                // numeric content (<v>notanumber</v>) that makes real Excel\n                // refuse the whole file (0x800A03EC) while schema validation\n                // stays green. Reject up front, mirroring the boolean/date\n                // guards above.\n                if (cellType.ToLowerInvariant() is \"number\" or \"num\")\n                {\n                    var numText = cell.CellValue?.Text?.Trim();\n                    if (!string.IsNullOrEmpty(numText)\n                        && (!double.TryParse(numText, System.Globalization.NumberStyles.Any,\n                                System.Globalization.CultureInfo.InvariantCulture, out var numDbl)\n                            || !double.IsFinite(numDbl)))\n                        throw new ArgumentException(\n                            $\"Cannot store '{cell.CellValue?.Text}' as number; value must be a finite numeric literal. \" +\n                            \"Use type=string to keep the literal text.\");\n                }\n                // CONSISTENCY(cell-type-parity): mirror Set's value auto-detect\n                // path (ExcelHandler.Set.cs lines 1025-1033) — parse the cell\n                // value as an ISO date and write it back as an OADate double so\n                // Excel renders it as a real date instead of a literal string.\n                if (cellType.Equals(\"date\", StringComparison.OrdinalIgnoreCase))\n                {\n                    var dateText = cell.CellValue?.Text?.Trim();\n                    // R13-2: accept ISO date-with-time (T separator) as well.\n                    if (!string.IsNullOrEmpty(dateText)\n                        && TryParseIsoDateFlexible(dateText, out var dt))\n                    {\n                        // Mirrors Set's pre-1900 guard: Excel's serial epoch is\n                        // 1899-12-30; earlier dates round-trip as the epoch and\n                        // mislead the user. Reject instead of silently clamping.\n                        if (dt < new System.DateTime(1900, 1, 1))","sourceCodeStart":705,"sourceCodeEnd":741,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Add.Cells.cs#L705-L741","documentation":"A type=number cell stores its value in <v> with no t= attribute, so a non-numeric value produces spec-invalid numeric content (<v>notanumber</v>) that makes real Excel refuse the whole file (0x800A03EC) while schema validation stays green. This guard parses the cell text with double.TryParse (InvariantCulture, Any NumberStyles) and rejects non-finite or unparseable values up front, mirroring the boolean/date guards.","triggerScenarios":"Add(\"/Sheet1/A1\",\"cell\",pos,{[\"type\"]=\"number\",[\"value\"]=\"abc\"}); value=\"12,34\" in a locale expecting '.'; value=\"NaN\"/\"Infinity\" (not finite); value=\"1.2.3\"; type=num value=\"\".","commonSituations":"Forcing a text column into numeric type; locale-specific number formatting (thousands separators, comma decimals) that double.TryParse under InvariantCulture rejects; passing Infinity/NaN from a computation.","solutions":["Pass a finite numeric literal using a period as the decimal separator (InvariantCulture format), e.g. \"3.14\".","If the value may be text, use type=string instead of type=number.","Sanitize locale-specific formatting (strip thousands separators, convert comma decimals) before the call."],"exampleFix":"// before\nhandler.Add(\"/Sheet1/A1\", \"cell\", null, new() { [\"type\"] = \"number\", [\"value\"] = \"1.234,56\" });\n// after\nvar num = double.Parse(\"1.234,56\", CultureInfo.GetCultureInfo(\"de-DE\"));\nhandler.Add(\"/Sheet1/A1\", \"cell\", null, new() { [\"type\"] = \"number\", [\"value\"] = num.ToString(CultureInfo.InvariantCulture) });","handlingStrategy":"validation","validationCode":"if (props.GetValueOrDefault(\"type\")?.ToLowerInvariant() is \"number\" or \"num\")\n{\n    var n = props.GetValueOrDefault(\"value\");\n    if (!string.IsNullOrEmpty(n) && (!double.TryParse(n, NumberStyles.Any, CultureInfo.InvariantCulture, out var d) || !double.IsFinite(d)))\n        throw new ArgumentException(\"value is not a finite numeric literal\");\n    props[\"value\"] = double.Parse(n!, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture);\n}\nh.Add(parentPath, \"cell\", pos, props);","typeGuard":"static bool IsFiniteNumeric(string? s) =>\n    !string.IsNullOrEmpty(s) && double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var d) && double.IsFinite(d);","tryCatchPattern":"try { h.Add(parentPath, \"cell\", pos, props); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"as number\"))\n{ /* fall back to type=string or sanitize locale formatting */ }","preventionTips":["Use InvariantCulture decimal format (period separator) for numeric values.","Reject NaN/Infinity before the call; do not pass them as numbers.","Sanitize locale-specific separators (thousands, comma decimals) first."],"tags":["excel","xlsx","cell","number","locale","validation"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}