{"record":{"id":"3fa407b1a8f3c7cd","repo":"gitbutlerapp/gitbutler","slug":"failed-to-create-pull-request-comment","errorCode":null,"errorMessage":"Failed to create pull request comment: {}","messagePattern":"Failed to create pull request comment: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-github/src/client.rs","lineNumber":1398,"sourceCode":"        #[derive(Serialize)]\n        struct CommentBody<'a> {\n            body: &'a str,\n        }\n\n        let url = format!(\n            \"{}/repos/{}/{}/issues/{}/comments\",\n            self.base_url, owner, repo, pr_number\n        );\n\n        let response = self\n            .client\n            .post(&url)\n            .json(&CommentBody { body })\n            .send()\n            .await?;\n\n        if !response.status().is_success() {\n            bail!(\n                \"Failed to create pull request comment: {}\",\n                response_error(response).await\n            );\n        }\n\n        let comment: GitHubIssueComment = response.json().await?;\n        Ok(comment.into())\n    }\n\n    /// Update the information of a given PR.\n    ///\n    /// This is used e.g. to update the description footers for stacked reviews.\n    pub async fn update_pull_request(\n        &self,\n        params: &UpdatePullRequestParams<'_>,\n    ) -> Result<PullRequest> {\n        #[derive(Serialize)]\n        struct UpdatePullRequestBody<'a> {","sourceCodeStart":1380,"sourceCodeEnd":1416,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-github/src/client.rs#L1380-L1416","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Read the embedded status/body: 403 with 'locked' means comments are disabled — skip posting instead of retrying.","Validate the body is non-empty before calling; trim whitespace and reject blank comments client-side.","Verify the token has write access to issues/conversations and the repo coordinates are correct.","Respect `Retry-After`/rate-limit headers and retry later with backoff for 403 rate-limit or 5xx.","Confirm the issue number is the PR's number in that exact repository."],"exampleFix":"// before\nlet body = body.trim();\nlet comment = client.create_pull_request_comment(owner, repo, pr, body).await?; // 422 on empty\n// after\nlet body = body.trim();\nif body.is_empty() {\n    return Ok(None); // nothing to post\n}\nlet comment = client.create_pull_request_comment(owner, repo, pr, body).await?;","handlingStrategy":"validation","validationCode":"if body.trim().is_empty() { return Err(anyhow!(\"comment body must not be empty\")); }\nlet pr = client.get_pull_request(owner, repo, pr_number).await?; // 404 check\nif client.issue_comments_locked(owner, repo).await.unwrap_or(false) {\n    return Err(anyhow!(\"comments are locked on {owner}/{repo}\"));\n}","typeGuard":"fn postable(body: &str) -> bool { !body.trim().is_empty() }","tryCatchPattern":"match client.create_pull_request_comment(owner, repo, pr, body).await {\n    Ok(c) => c,\n    Err(e) if e.to_string().contains(\"403\") && e.to_string().contains(\"locked\") => {\n        warn!(\"comments locked; skipping\");\n        return Err(e);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Reject empty/whitespace comment bodies before the API call.","Check that the repo does not lock/disable issue comments.","Honor rate-limit headers and back off instead of hammering retries.","Verify token scope covers issues/conversations write."],"tags":["github-api","http-error-response","rest","network"],"backgroundTag":"http-error-response","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}