{"record":{"id":"06a536185c37bb4e","repo":"gitbutlerapp/gitbutler","slug":"failed-to-update-pull-request-status-error-t","errorCode":null,"errorMessage":"Failed to update pull request: {status} - {error_text}","messagePattern":"Failed to update pull request: (.+?) - (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-bitbucket/src/client.rs","lineNumber":380,"sourceCode":"    async fn send_pr_update(\n        &self,\n        workspace: &str,\n        repo_slug: &str,\n        id: i64,\n        body: &UpdatePullRequestBody<'_>,\n    ) -> Result<BitbucketPullRequest> {\n        let url = format!(\n            \"{}/repositories/{}/{}/pullrequests/{}\",\n            self.base_url,\n            urlencoding::encode(workspace),\n            urlencoding::encode(repo_slug),\n            id,\n        );\n        let response = self.client.put(&url).json(body).send().await?;\n        if !response.status().is_success() {\n            let status = response.status();\n            let error_text = response.text().await.unwrap_or_default();\n            bail!(\"Failed to update pull request: {status} - {error_text}\");\n        }\n        let pr: BitbucketApiPullRequest = response.json().await?;\n        Ok(pr.into())\n    }\n\n    pub async fn update_pull_request(\n        &self,\n        params: &UpdatePullRequestParams<'_>,\n    ) -> Result<BitbucketPullRequest> {\n        let ctx = self\n            .fetch_pr_edit_context(params.workspace, params.repo_slug, params.id)\n            .await?;\n        let body = build_update_body(\n            &ctx,\n            params.title,\n            params.description,\n            params.target_branch,\n            None,","sourceCodeStart":362,"sourceCodeEnd":398,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-bitbucket/src/client.rs#L362-L398","documentation":"Raised by update_pull_request's underlying PUT to /repositories/{workspace}/{repo_slug}/pullrequests/{id} when Bitbucket answers non-2xx; the message carries both the status code and the raw response body (error_text), which usually contains Bitbucket's JSON error detail naming the invalid field or unmet precondition.","triggerScenarios":"Editing title/description/reviewers of a PR that is no longer OPEN (MERGED/DECLINED PRs reject edits), sending an invalid payload (empty title, unknown reviewer UUID), a token without pullrequest write scope, or a 409 from editing a PR whose version moved concurrently.","commonSituations":"UI holding a stale PR view after someone merged/declined it elsewhere; automation editing PRs after CI merges them; token scopes trimmed after a security review.","solutions":["Read {error_text}: Bitbucket names the exact problem (e.g. field validation or state error)","Re-fetch the PR (list/get) and only call update when values.state == OPEN","Re-authenticate with 'but config forge auth' if the status is 401/403 so the token gets pullrequest write scope","On 409/version conflict, re-fetch, re-apply the edit on the fresh version, and retry once"],"exampleFix":"// before: blind edit on a possibly-stale PR\nclient.update_pull_request(&params).await?;\n\n// after: refresh state, edit only while OPEN\nlet pr = client.get_pull_request(ws, slug, id).await?;\nif pr.state != \"OPEN\" { anyhow::bail!(\"cannot edit {} PR\", pr.state); }\nclient.update_pull_request(&params).await?;","handlingStrategy":"try-catch","validationCode":"// only edit PRs that are currently OPEN\nlet pr = client.get_pull_request(ws, slug, id).await?;\nanyhow::ensure!(pr.state == \"OPEN\", \"PR {id} is {} — not editable\", pr.state);","typeGuard":null,"tryCatchPattern":"match client.update_pull_request(&params).await {\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.contains(\"409\") { reapply_on_fresh_version(&client, &params).await?; }\n        else { return Err(e.context(\"update PR\")); }\n    }\n    ok => ok,\n}","preventionTips":["Refresh PR state immediately before editing; never reuse a view older than seconds in busy repos","Disable the edit button for non-OPEN PRs in the UI","Keep the raw error_text visible to users — Bitbucket names the invalid field"],"tags":["bitbucket","pull-request","http","merge-gate"],"backgroundTag":"pull-request-update-failed","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}