{"record":{"id":"006165d00a4e20a5","repo":"iOfficeAI/OfficeCLI","slug":"property-ref-is-required-for-comment","errorCode":null,"errorMessage":"Property 'ref' is required for comment","messagePattern":"Property 'ref' is required for comment","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Add.Tables.cs","lineNumber":253,"sourceCode":"\n        var nrIdx = PathIndex.FromArrayIndex(definedNames.Elements<DefinedName>().ToList().IndexOf(dn));\n        return $\"/namedrange[{nrIdx}]\";\n    }\n\n    private string AddComment(string parentPath, string type, InsertPosition? position, Dictionary<string, string> properties)\n    {\n        var index = position?.Index;\n        var cmtSegments = parentPath.TrimStart('/').Split('/', 2);\n        var cmtSheetName = cmtSegments[0];\n        // Extract cell reference from path if present (e.g., /Sheet1/A1 -> A1)\n        string? cmtRefFromPath = null;\n        if (cmtSegments.Length > 1 && Regex.IsMatch(cmtSegments[1], @\"^[A-Z]+\\d+$\", RegexOptions.IgnoreCase))\n            cmtRefFromPath = cmtSegments[1];\n        var cmtWorksheet = FindWorksheet(cmtSheetName)\n            ?? throw new ArgumentException($\"Sheet not found: {cmtSheetName}\");\n\n        var cmtRef = properties.GetValueOrDefault(\"ref\") ?? cmtRefFromPath\n            ?? throw new ArgumentException(\"Property 'ref' is required for comment\");\n        // Validate cell reference up-front; ParseCellReference rejects bad\n        // syntax, out-of-range rows (>1048576), and out-of-range columns (>XFD)\n        // with a clear ArgumentException — matches the validation surface\n        // already enforced for cells/ranges elsewhere.\n        ParseCellReference(cmtRef);\n        var cmtText = properties.GetValueOrDefault(\"text\", \"\");\n        var cmtAuthor = properties.GetValueOrDefault(\"author\", \"Author\");\n        OfficeCli.Core.ParseHelpers.ValidateXmlText(cmtText, \"comment text\");\n        OfficeCli.Core.ParseHelpers.ValidateXmlText(cmtAuthor, \"comment author\");\n\n        var commentsPart = cmtWorksheet.WorksheetCommentsPart\n            ?? cmtWorksheet.AddNewPart<WorksheetCommentsPart>();\n\n        if (commentsPart.Comments == null)\n        {\n            commentsPart.Comments = new Comments(\n                new Authors(new Author(cmtAuthor)),\n                new CommentList()","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Add.Tables.cs#L235-L271","documentation":"Thrown by AddComment when no cell reference can be resolved. cmtRef is chosen from properties[\"ref\"], else a cell-like second path segment (matching ^[A-Z]+\\d+$), else this error. So it fires only when neither a ref property nor a /Sheet/Cell path is supplied.","triggerScenarios":"Call Add type \"comment\" with parentPath \"/Sheet1\" (no cell segment) and a properties dictionary that has no \"ref\" key.","commonSituations":"Forgetting the --prop ref=A1 argument; passing only the sheet path expecting the API to pick a cell; building the properties dict programmatically and skipping the ref key.","solutions":["Add properties[\"ref\"] = \"A1\" (any valid A1 cell).","Or include the cell in the path as \"/Sheet1/A1\".","Validate ParseCellReference on the chosen ref before calling, to also catch out-of-range columns/rows."],"exampleFix":"// before\nhandler.Add(\"/Sheet1\", \"comment\", null, new() { [\"text\"] = \"note\" });\n// after\nhandler.Add(\"/Sheet1\", \"comment\", null,\n    new() { [\"ref\"] = \"B2\", [\"text\"] = \"note\" });","handlingStrategy":"validation","validationCode":"var seg = parentPath.TrimStart('/').Split('/', 2);\nstring? refFromPath = (seg.Length > 1 && Regex.IsMatch(seg[1], @\"^[A-Z]+\\d+$\", RegexOptions.IgnoreCase)) ? seg[1] : null;\nstring cmtRef = properties.GetValueOrDefault(\"ref\") ?? refFromPath\n    ?? throw new InvalidOperationException(\"comment needs ref= or /Sheet/Cell path\");","typeGuard":"static bool HasCommentTarget(string parentPath, Dictionary<string,string> p)\n{\n    if (p.ContainsKey(\"ref\")) return true;\n    var seg = parentPath.TrimStart('/').Split('/', 2);\n    return seg.Length > 1 && Regex.IsMatch(seg[1], @\"^[A-Z]+\\d+$\", RegexOptions.IgnoreCase);\n}","tryCatchPattern":null,"preventionTips":["Always pass ref= explicitly even when the path already contains a cell.","Validate the cell ref shape before calling (catches out-of-range rows/columns too).","When building properties programmatically, assert the ref key is set."],"tags":["excel","comment","required-property","validation"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}