{"record":{"id":"158e4066ed42349e","repo":"iOfficeAI/OfficeCLI","slug":"invalid-range-spec-expected-start-end-with","errorCode":null,"errorMessage":"Invalid range '{spec}'. Expected 'start:end' with 0-based integer character offsets (e.g. '6:11').","messagePattern":"Invalid range '(.+?)'\\. Expected 'start:end' with 0-based integer character offsets \\(e\\.g\\. '6:11'\\)\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/ParseHelpers.cs","lineNumber":437,"sourceCode":"    {\n        if (!int.TryParse(value, CultureInfo.InvariantCulture, out var result))\n            throw new ArgumentException($\"Invalid '{propertyName}' value '{value}'. Expected an integer.\");\n        return result;\n    }\n\n    /// <summary>\n    /// Parse a \"start:end\" character-range spec into 0-based, half-open offsets.\n    /// Colon separator mirrors the officecli range convention (Excel A1:B2).\n    /// Shared by the pptx and docx run-range formatting paths so the two never\n    /// diverge (CONSISTENCY(char-range)).\n    /// </summary>\n    public static (int Start, int End) ParseCharRange(string spec)\n    {\n        var parts = spec.Split(':');\n        if (parts.Length != 2\n            || !int.TryParse(parts[0].Trim(), CultureInfo.InvariantCulture, out var start)\n            || !int.TryParse(parts[1].Trim(), CultureInfo.InvariantCulture, out var end))\n            throw new ArgumentException(\n                $\"Invalid range '{spec}'. Expected 'start:end' with 0-based integer \" +\n                \"character offsets (e.g. '6:11').\");\n        if (start < 0 || end < 0)\n            throw new ArgumentException($\"Invalid range '{spec}': offsets must be non-negative.\");\n        if (end < start)\n            throw new ArgumentException($\"Invalid range '{spec}': end ({end}) must be >= start ({start}).\");\n        return (start, end);\n    }\n\n    /// <summary>\n    /// Parse a comma-separated list of \"start:end\" character ranges into 0-based,\n    /// half-open offset pairs (e.g. \"6:11,20:25\" → [(6,11),(20,25)]). A single\n    /// range needs no comma. This lets one range= command target several disjoint\n    /// spans — the same shape find's format path produces from multiple matches —\n    /// so range is a complete addressing alternative wherever the caller already\n    /// knows the offsets. Order is preserved; the caller applies them (format-only\n    /// run splitting does not shift character offsets, so any order is safe).\n    /// </summary>","sourceCodeStart":419,"sourceCodeEnd":455,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/ParseHelpers.cs#L419-L455","documentation":"Thrown by ParseCharRange when the spec is not exactly two colon-separated integer tokens. A character range must be 'start:end' with 0-based integer offsets (e.g. '6:11'). Wrong separators, missing a side, or non-integer tokens all trigger this.","triggerScenarios":"Passing \"6-11\" (dash), \"6..11\", \"6\" (missing end), \"6:11:20\" (too many parts), or \"abc:def\". Also \"6 : 11\" is fine because each side is trimmed, but \"6;11\" is not.","commonSituations":"Copy-pasting a range from a tool that uses a different delimiter; building the spec by joining with the wrong character; off-by formatting from find's match offsets expressed differently.","solutions":["Format the spec as start:end with a single colon, e.g. $\"{start}:{end}\".","Ensure both sides are integers (no units, no decimals).","If you have a list, use the multi-range parser with commas between ranges."],"exampleFix":"// before\nvar spec = $\"{start}-{end}\";\nParseCharRange(spec); // throws 298\n\n// after\nvar spec = $\"{start}:{end}\";\nParseCharRange(spec);","handlingStrategy":"validation","validationCode":"var parts = spec.Split(':');\nif (parts.Length != 2\n    || !int.TryParse(parts[0].Trim(), CultureInfo.InvariantCulture, out _)\n    || !int.TryParse(parts[1].Trim(), CultureInfo.InvariantCulture, out _))\n    spec = $\"0:0\";","typeGuard":"static bool IsCharRange(string spec)\n{ var p = spec.Split(':');\n  return p.Length == 2\n      && int.TryParse(p[0].Trim(), CultureInfo.InvariantCulture, out _)\n      && int.TryParse(p[1].Trim(), CultureInfo.InvariantCulture, out _); }","tryCatchPattern":"try { range = ParseCharRange(spec); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"Expected 'start:end'\"))\n{ /* rebuild spec with colon separator */ }","preventionTips":["Build range specs with $\"{start}:{end}\".","Keep a single colon as the only separator.","Validate offsets are integers before formatting."],"tags":["range","validation","argument","format"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}