gitbutlerapp/gitbutler · error

Failed to create pull request comment

Error message

Failed to create pull request comment: {}

What it means

This error comes from the PR comment creation path: POSTing `/repos/{owner}/{repo}/issues/{pr_number}/comments` (or the equivalent PR comment endpoint) with a `CommentBody`. Any non-success status is wrapped via `response_error` and bailed, so no `PullRequestComment` is produced and the comment was not created.

Solutions

  1. Read the embedded status/body: 403 with 'locked' means comments are disabled — skip posting instead of retrying.
  2. Validate the body is non-empty before calling; trim whitespace and reject blank comments client-side.
  3. Verify the token has write access to issues/conversations and the repo coordinates are correct.
  4. Respect `Retry-After`/rate-limit headers and retry later with backoff for 403 rate-limit or 5xx.
  5. Confirm the issue number is the PR's number in that exact repository.

Example fix

// before
let body = body.trim();
let comment = client.create_pull_request_comment(owner, repo, pr, body).await?; // 422 on empty
// after
let body = body.trim();
if body.is_empty() {
    return Ok(None); // nothing to post
}
let comment = client.create_pull_request_comment(owner, repo, pr, body).await?;
Defensive patterns

Strategy: validation

Validate before calling

if body.trim().is_empty() { return Err(anyhow!("comment body must not be empty")); }
let pr = client.get_pull_request(owner, repo, pr_number).await?; // 404 check
if client.issue_comments_locked(owner, repo).await.unwrap_or(false) {
    return Err(anyhow!("comments are locked on {owner}/{repo}"));
}

Type guard

fn postable(body: &str) -> bool { !body.trim().is_empty() }

Try / catch

match client.create_pull_request_comment(owner, repo, pr, body).await {
    Ok(c) => c,
    Err(e) if e.to_string().contains("403") && e.to_string().contains("locked") => {
        warn!("comments locked; skipping");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Creating a comment on a PR when GitHub returns non-2xx — 404 (wrong owner/repo/issue number), 403 (issues/conversations write denied, disabled issue comments, or rate limit), 401 (invalid token), 422 (empty or invalid body).

Common situations: Posting to a repo where issue comments are locked/disabled by maintainers; token without `repo` scope on a private repo; empty comment body after trimming; PR number mixed up with an internal stack/branch id; rate limit exhausted after a bulk operation.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/3fa407b1a8f3c7cd. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-github/src/client.rs:1398

        #[derive(Serialize)]
        struct CommentBody<'a> {
            body: &'a str,
        }

        let url = format!(
            "{}/repos/{}/{}/issues/{}/comments",
            self.base_url, owner, repo, pr_number
        );

        let response = self
            .client
            .post(&url)
            .json(&CommentBody { body })
            .send()
            .await?;

        if !response.status().is_success() {
            bail!(
                "Failed to create pull request comment: {}",
                response_error(response).await
            );
        }

        let comment: GitHubIssueComment = response.json().await?;
        Ok(comment.into())
    }

    /// Update the information of a given PR.
    ///
    /// This is used e.g. to update the description footers for stacked reviews.
    pub async fn update_pull_request(
        &self,
        params: &UpdatePullRequestParams<'_>,
    ) -> Result<PullRequest> {
        #[derive(Serialize)]
        struct UpdatePullRequestBody<'a> {

View on GitHub (pinned to 58e5313667)