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

  1. Remove the existing comment first (Remove on /Sheet/Cell or the comment path).
  2. Target a different cell if you intend to keep the old comment.
  3. 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

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


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/17d33ef83e8c1210. Report an issue: GitHub.