Open-source project
peter-evans/create-or-update-comment avatar
peter-evans/create-or-update-comment

create-or-update-comment: one action for the bot comment lifecycle

A GitHub action to create or update an issue or pull request comment

933 stars156 forksTypeScriptMIT

At a glance

What is it?
create-or-update-comment is a GitHub action that creates a comment on an issue or pull request and updates that same comment later, with append or replace modes, reaction support and an output carrying the comment id. It solves a problem that looks trivial until a workflow has to be idempotent.
Who is it for?
create-or-update-comment suits repository maintainers whose workflows post status, build output or review notes to issues and pull requests and need the same comment to be updated rather than duplicated on each run. It does not suit a workflow that must comment on pull requests opened from forks in a public repository, since GitHub token restrictions make that fail with Resource not accessible by integration unless you move to pull_request_target.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 11 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A comment that can be created and then revised

create-or-update-comment is a GitHub action to create or update an issue or pull request comment, and its whole reason to exist is the second half of that sentence.

Almost every workflow that reports something back to an issue or pull request ends up needing to write the same comment twice: once when the job starts or finishes, and again later with more information. Writing it by hand means calling the API with a create and then a separate update, tracking the comment id yourself, and cleaning up after a re-run. This action does that bookkeeping and exposes the id as a step output.

The usage is a normal action reference in a workflow file. The inputs are given as a with block, and the action is pinned to a major version:

yml
      - name: Create comment
        uses: peter-evans/create-or-update-comment@v5
        with:
          issue-number: 1
          body: |
            My comment

To revise an existing comment, you pass comment-id instead of relying on creation, and you choose how the new body relates to the old one through edit-mode, which is either replace or append and defaults to append. The append-separator input controls what goes between the old and new text and accepts newline, space or none.

That default is worth pausing on, because it changes behaviour quietly. Append is the default, so a workflow that runs repeatedly keeps stacking text on the comment unless it explicitly asks for replace.

Finding the comment id is the hard part

The action's output section says the id of the created comment is output for use in later steps, and notes that in order to read the step output the action step must have an id:

yml
      - name: Create comment
        uses: peter-evans/create-or-update-comment@v5
        id: couc
        with:
          issue-number: 1
          body: |
            My comment
      - name: Check outputs
        run: |
          echo "Comment ID - ${{ steps.couc.outputs.comment-id }}"

The README is candid that where the id comes from depends on the use case. One route is the github context during an issue_comment event:

yml
on:
  issue_comment:
    types: [created]
jobs:
  commentCreated:
    runs-on: ubuntu-latest
    steps:
      - name: Add reaction
        uses: peter-evans/create-or-update-comment@v5
        with:
          comment-id: ${{ github.event.comment.id }}
          reactions: eyes

The other route is a search, and here the README points at a companion action, find-comment, which searches an issue or pull request for the first comment containing a specified string and/or by a specified author. The typical pattern is to find the bot's own previous comment and feed the result back in:

yml
    - name: Find Comment
      uses: peter-evans/find-comment@v3
      id: fc
      with:
        issue-number: ${{ github.event.pull_request.number }}
        comment-author: 'github-actions[bot]'
        body-includes: Build output

If that search returns an empty string the comment does not exist yet, and if it returns a value the content can be replaced. The README also shows the alternative of splitting create and update into two steps guarded by an if condition on the found id, which gives finer control than passing an empty id into one step.

Fork pull requests break the obvious setup

The limitation is stated plainly in the README: in public repositories this action does not work in pull_request workflows when triggered by forks, and any attempt is met with the error Resource not accessible by integration. The cause is token restrictions put in place by GitHub Actions.

This is a security boundary rather than a bug. A workflow triggered by pull_request from a fork runs with a restricted token precisely so that an outside contributor cannot write to the repository, and the same restriction blocks the comment write. The error message is confusing when you hit it cold, because the action and the workflow both look correct.

There are two documented workarounds. Private repositories can be configured to enable workflows from forks to run without restriction, through the repository settings for enabling workflows for forks of private repositories. For public repositories, the README points to the pull_request_target event as the alternative for commenting on pull requests.

pull_request_target is not a free fix. It runs the workflow with the base repository's token and secrets in a context that an untrusted fork can influence, so pairing it with this action means being careful about what else runs in the same job and about which context values you interpolate into the comment body. The action does not make that decision for you, and the README does not walk through it.

Reactions as a small status channel

The reactions input takes a comma or newline separated list of reactions to add to the comment, and the documented set is +1, -1, laugh, confused, heart, hooray, rocket and eyes. A separate reactions-edit-mode input mirrors edit-mode, offering replace or append and defaulting to append.

The use is subtle but useful. A reaction is a structured, low-noise signal attached to a comment, and the README example uses it to mark a comment from a bot with the eyes reaction, which is how a human scanning a busy pull request spots the one they care about.

The reactions-edit-mode default of append deserves the same attention as edit-mode. Appended reactions accumulate, so a workflow that runs on every push will end up with a comment carrying the same reaction many times, which GitHub displays as a count rather than a distinct set. A workflow that wants a stable indicator on the comment should set reactions-edit-mode to replace so the reaction set reflects the current state rather than the history.

Since a comment id can come straight from the event context in an issue_comment workflow, adding a reaction does not require the create step at all, which is why the reaction example is the shortest of the three usage snippets.

Repository inputs for cross-repository comments

Two inputs widen the scope beyond the current repository and the default token.

The token input accepts GITHUB_TOKEN, which the README qualifies as needing issues: write and pull-requests: write permissions, or a repo scoped personal access token. The default is GITHUB_TOKEN.

The repository input is the full name of the repository in which to create or update a comment, and it defaults to the current repository. Combined with the PAT option, this is what lets a workflow in one repository comment on an issue in another, which is how a central bot repository reports on many downstream projects.

The body input takes the comment text directly and cannot be used in conjunction with body-path, which instead takes the path to a file containing the body. That pair exists for the common case where the comment content is generated by an earlier step and written to a file, since passing a multi-line string through YAML is where quoting mistakes happen.

The full input list also covers the append-separator and the two edit modes, so the behaviour of every write is controlled by named inputs rather than by convention. The README does not document an input for deleting a comment, so a workflow that wants to remove stale output has to call the API itself.

TypeScript source shipped as a compiled dist bundle

The repository layout shows a TypeScript project with the usual action shape: action.yml at the top level, src/ for the source, dist/ for the compiled bundle that GitHub Actions actually runs, plus .eslintrc.json, .prettierrc.json, jest.config.js, tsconfig.json and package-lock.json.

The package.json states the important details. The version is 5.0.0, the main entry is lib/main.js, and the engines field requires node >=24.4.0. The runtime dependencies are only two: @actions/core and @actions/github, which is the standard toolset for reading inputs, writing outputs and calling the GitHub API from an action.

The build script is tsc followed by ncc build, which is how TypeScript actions produce the self-contained bundle in dist/ that GitHub requires, since a workflow does not run npm install before your step. The lint, format and test scripts wrap eslint, prettier and jest, and jest is configured with --passWithNoTests, which tells you the test suite is allowed to be empty.

On maintenance, the repository is not archived and the last push was on 2026-10-12. The releases tell a different story about the version line: v5.0.0 was published on 2025-10-02, preceded by v4.0.0 on 2024-01-25 and v3.1.0 on 2023-10-19. So the current major version has been out for about a year with commits landing since, which is the normal shape for an action that is stable rather than churning. The licence field on the repository says MIT, and the LICENSE file sits at the top level.

Editorial conclusion

create-or-update-comment suits repository maintainers whose workflows post status, build output or review notes to issues and pull requests and need the same comment to be updated rather than duplicated on each run. It does not suit a workflow that must comment on pull requests opened from forks in a public repository, since GitHub token restrictions make that fail with Resource not accessible by integration unless you move to pull_request_target. Before adopting, decide whether you need append or replace, pin the major version, and test the fork case with your own repository, because the restriction applies at the point where the comment is written.

Frequently asked questions

How do I create or update a GitHub comment in a workflow?

Use peter-evans/create-or-update-comment@v5 with issue-number and a body to create one, and comment-id with an edit-mode of either replace or append to update an existing comment. The default edit-mode is append, with a newline separator.

Why does create-or-update-comment fail with Resource not accessible by integration?

In public repositories the action does not work in pull_request workflows triggered by forks, because GitHub Actions token restrictions block the write. The README points to enabling workflows from forks in private repositories, or to using the pull_request_target event as the alternative.

How do I find the id of a comment to update it?

The action outputs the id of the comment it created, readable when the step has an id. For an existing comment you can use the github.event.comment.id context in an issue_comment workflow, or the companion find-comment action, which searches an issue or pull request for the first comment containing a given string and/or by a given author.

What reactions can create-or-update-comment add?

The reactions input takes a comma or newline separated list, and the documented set is +1, -1, laugh, confused, heart, hooray, rocket and eyes. The reactions-edit-mode input controls whether the reaction set is replaced or appended to, and defaults to append.

Official sources

  1. Issues
  2. License: MIT
  3. peter-evans/create-or-update-comment on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/peter-evans-create-or-update-comment.svg)](https://hysenlabs.com/projects/peter-evans-create-or-update-comment)

Was this useful?