Skip to content

Remove Comments

Document.RemoveComment(id) deletes a comment, the range markers and reference run that anchor it in the body, and its threading metadata. Removing a thread root takes the whole thread with it, matching Word, which never leaves a reply pointing at a comment that is gone.

The call returns a bool, not an error. It is false when the document has no comments part or when no comment carries that ID, and true otherwise.

Removing the first thread

doc, err := document.Open("sample.docx")
if err != nil {
    return err
}
defer doc.Close()

for _, c := range doc.Comments() {
    if c.IsReply() {
        continue
    }
    if ok := doc.RemoveComment(c.ID()); !ok {
        return fmt.Errorf("failed removing comment %d", c.ID())
    }
    break
}

The break is load-bearing. Read on for why a loop that keeps going here would remove the wrong comments.

IDs are renumbered after every removal

Once a removal completes, the comments that remain are renumbered sequentially from 1 in comments.xml order, and the range markers and reference runs in the body are rewritten to match. Any ID you captured before the call is now meaningless: it either refers to a different comment or to none at all.

So collecting a list of IDs and then removing them one by one does not work. Re-read Document.Comments() after each removal, or remove in a loop that takes one ID at a time from a freshly read slice.

Limitations

Removing a reply leaves its root and the other replies in place; only removal of a root cascades.

When the last comment goes, comments.xml, commentsExtended.xml, commentsIds.xml and commentsExtensible.xml are dropped from the package along with their relationships and content-type overrides, and HasComments() starts returning false.

Anchors are cleaned out of every story that can hold one: the body, table cells, headers, footers, footnotes and endnotes. The text the comment covered is left untouched; only the markup wrapping it is removed.

Run the example

The example opens the bundled sample.docx, prints its comments, removes the first thread root it finds with firstThreadRoot, and prints the list again so the renumbering is visible in the output. It reports the result to stdout and does not write the document back; add a doc.SaveToFile call to keep the change.

git clone https://github.com/unidoc/unioffice-examples.git
cd unioffice-examples/document/comment-remove
go run main.go

If this is your first time using UniOffice, follow the getting started guide to create an API key and set up your development environment.

View the full source
Last updated on