iOfficeAI/OfficeCLI · error · ArgumentException
comment already exists on {cmtRefUpper}. Remove it first bef
Error message
comment already exists on {cmtRefUpper}. Remove it first before adding a new comment. What it means
Thrown by AddComment after the comments part is located. It upper-cases the target ref and scans the existing CommentList for any Comment whose Reference equals it (OrdinalIgnoreCase). A duplicate is ambiguous, so the handler mirrors its table-overlap T4 reject pattern and requires the caller to remove the old comment first.
Source
Thrown at src/officecli/Handlers/Excel/ExcelHandler.Add.Tables.cs:285
if (commentsPart.Comments == null)
{
commentsPart.Comments = new Comments(
new Authors(new Author(cmtAuthor)),
new CommentList()
);
}
var comments = commentsPart.Comments;
var authors = comments.GetFirstChild<Authors>()!;
var commentList = comments.GetFirstChild<CommentList>()!;
// CONSISTENCY(overlap-reject): duplicate comment on the same
// cell is ambiguous — mirror the table T4 overlap-reject
// pattern. User must `remove comment` first to replace it.
var cmtRefUpper = cmtRef.ToUpperInvariant();
if (commentList.Elements<Comment>().Any(c =>
string.Equals(c.Reference?.Value, cmtRefUpper, StringComparison.OrdinalIgnoreCase)))
throw new ArgumentException(
$"comment already exists on {cmtRefUpper}. Remove it first before adding a new comment.");
uint authorId = 0;
var existingAuthors = authors.Elements<Author>().ToList();
var authorIdx = existingAuthors.FindIndex(a => a.Text == cmtAuthor);
if (authorIdx >= 0)
authorId = (uint)authorIdx;
else
{
authors.AppendChild(new Author(cmtAuthor));
authorId = (uint)existingAuthors.Count;
}
var comment = new Comment { Reference = cmtRef.ToUpperInvariant(), AuthorId = authorId };
// Support user-supplied `\n` (literal two-char sequence from
// CLI) and real LF as line breaks — Excel renders the
// preserved newline in the comment body. Matches the shape
// `text` behavior documented in add-shape help.View on GitHub (pinned to 1ced45e900)
Solutions
- Remove the existing comment first (Remove on /Sheet/Cell or the comment path).
- Target a different cell if you intend to keep the old comment.
- Track annotated cells and skip them, or branch on existence before adding.
Example fix
// before (cell already has a comment)
handler.Add("/Sheet1/B2", "comment", null, new() { ["text"] = "new" });
// after
handler.Remove("/Sheet1/B2/comment");
handler.Add("/Sheet1/B2", "comment", null, new() { ["text"] = "new" }); Defensive patterns
Strategy: try-catch
Validate before calling
string cell = (properties.GetValueOrDefault("ref") ?? pathCellSegment ?? "").ToUpperInvariant();
var node = handler.Get($"/{sheet}", 2);
// treat any existing /Sheet/Cell/comment child as 'already present'
if (node.Children.Any(c => c.Path.Equals($"/{sheet}/{cell}/comment", StringComparison.OrdinalIgnoreCase)))
throw new InvalidOperationException($"comment already on {cell}"); Try / catch
try { handler.Add("/Sheet1/B2", "comment", null, props); }
catch (ArgumentException ex) when (ex.Message.Contains("comment already exists on"))
{
handler.Remove("/Sheet1/B2/comment");
handler.Add("/Sheet1/B2", "comment", null, props); // replace
} Prevention
- Track which cells you have annotated and skip or remove-first.
- Make add-comment idempotent in your wrapper: remove-then-add when replacing.
- Before batch runs, dump existing comments and subtract from your target set.
When it happens
Trigger: Call Add type "comment" targeting a cell (by ref property or path segment) that already has a Comment element in that sheet's WorksheetCommentsPart.
Common situations: Re-running an add script without idempotency; assuming add replaces; iterating over cells and hitting one already annotated in a prior pass.
Related errors
- DataValidation sqref '{nr}' overlaps existing validation sqr
- Property 'ref' is required for comment
- Property 'sqref' (or 'range'/'ref') is required for validati
- AutoFilter range '{afRangeUpper}' overlaps existing table '{
- invalid_value
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/17d33ef83e8c1210.
Report an issue: GitHub.