{"record":{"id":"7d0fe7b8cd14e9d7","repo":"iOfficeAI/OfficeCLI","slug":"criteria-colid-op-requires-lo-hi-got-rawv","errorCode":null,"errorMessage":"criteria{colId}.{op} requires 'lo,hi', got: '{rawVal}'","messagePattern":"criteria(.+?)\\.(.+?) requires 'lo,hi', got: '(.+?)'","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Add.Tables.cs","lineNumber":866,"sourceCode":"                    }\n                    case \"gt\":\n                        customEntries.Add((FilterOperatorValues.GreaterThan, rawVal));\n                        break;\n                    case \"gte\":\n                        customEntries.Add((FilterOperatorValues.GreaterThanOrEqual, rawVal));\n                        break;\n                    case \"lt\":\n                        customEntries.Add((FilterOperatorValues.LessThan, rawVal));\n                        break;\n                    case \"lte\":\n                        customEntries.Add((FilterOperatorValues.LessThanOrEqual, rawVal));\n                        break;\n                    case \"between\":\n                    case \"notbetween\":\n                    {\n                        var parts = rawVal.Split(',');\n                        if (parts.Length != 2)\n                            throw new ArgumentException(\n                                $\"criteria{colId}.{op} requires 'lo,hi', got: '{rawVal}'\");\n                        var lo = parts[0].Trim();\n                        var hi = parts[1].Trim();\n                        if (op == \"between\")\n                        {\n                            customEntries.Add((FilterOperatorValues.GreaterThanOrEqual, lo));\n                            customEntries.Add((FilterOperatorValues.LessThanOrEqual, hi));\n                            customFilterAnd = true;\n                        }\n                        else\n                        {\n                            // notBetween = lt lo OR gt hi (Excel default OR)\n                            customEntries.Add((FilterOperatorValues.LessThan, lo));\n                            customEntries.Add((FilterOperatorValues.GreaterThan, hi));\n                        }\n                        break;\n                    }\n                    case \"top\":","sourceCodeStart":848,"sourceCodeEnd":884,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Add.Tables.cs#L848-L884","documentation":"Thrown by AddAutoFilter when a 'between' or 'notBetween' filter criteria value does not split into exactly two comma-separated parts. The handler parses 'lo,hi' into a low and high bound to build Excel's customFilters (between = gte lo AND lte hi; notBetween = lt lo OR gt hi), so anything other than exactly one comma is unusable. It fails fast rather than emitting a malformed <customFilters> that Excel would silently drop.","triggerScenarios":"Calling Add (or `add --type autofilter`) with a property key matching ^criteria(\\d+).(between|notBetween)$ whose value has zero commas (e.g. criteria0.between=10), more than one comma (e.g. criteria0.between=10,20,30), or a trailing/leading comma only (e.g. criteria0.notBetween=,5).","commonSituations":"Users pasting a single bound expecting a one-sided range, locale confusion (comma used as decimal separator so '1,5' intended as 1.5), or copy-pasting a three-value range from a docs example.","solutions":["Provide exactly two comma-separated values: low first, high second, e.g. criteria0.between=10,100.","If you only have one bound, use gt/gte/lt/lte instead of between.","Confirm the value is not using a comma as a decimal separator; use a dot for decimals."],"exampleFix":"// before\n--prop criteria0.between=50\n// after\n--prop criteria0.between=10,100","handlingStrategy":"validation","validationCode":"// Validate a between/notBetween value has exactly two comma-separated bounds before calling Add.\nstatic bool TryValidateBetween(string op, string rawVal, out string error)\n{\n    error = null;\n    if (op is not (\"between\" or \"notbetween\")) return true;\n    var parts = rawVal.Split(',');\n    if (parts.Length != 2 || string.IsNullOrWhiteSpace(parts[0]) || string.IsNullOrWhiteSpace(parts[1]))\n    {\n        error = $\"criteria.{op} requires 'lo,hi', got: '{rawVal}'\";\n        return false;\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try { handler.Add(path, \"autofilter\", null, props); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"requires 'lo,hi'\"))\n{ /* surface to user: between/notBetween need two comma-separated bounds */ }","preventionTips":["Centralize criteria value validation in a helper that every caller uses before Add.","Treat between/notBetween specially in any UI: prompt for two separate bounds and join with a comma.","Reject empty bounds explicitly; Split(',') on '5,' yields 2 parts but the second is empty."],"tags":["excel","autofilter","validation","input-parsing"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}