{"record":{"id":"0e9f6126332f46c1","repo":"gitbutlerapp/gitbutler","slug":"could-not-restore-native-github-stack-membership-a","errorCode":null,"errorMessage":"Could not restore native GitHub stack membership after the push failed:\n{}","messagePattern":"Could not restore native GitHub stack membership after the push failed:\n(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-forge/src/review.rs","lineNumber":2971,"sourceCode":"    preferred_forge_user: &Option<crate::ForgeUser>,\n    forge_repo_info: &crate::forge::ForgeRepoInfo,\n    stacks: &[Vec<i64>],\n    storage: &but_forge_storage::Controller,\n) -> Result<()> {\n    let crate::forge::ForgeRepoInfo { owner, repo, .. } = forge_repo_info;\n    let preferred_account = preferred_forge_user.as_ref().and_then(|user| user.github());\n    let mut errors = Vec::new();\n    for pull_requests in stacks {\n        if let Err(err) =\n            but_github::stacks::create(preferred_account, owner, repo, pull_requests, storage).await\n        {\n            errors.push(format!(\"{err:#}\"));\n        }\n    }\n    if errors.is_empty() {\n        Ok(())\n    } else {\n        anyhow::bail!(\n            \"Could not restore native GitHub stack membership after the push failed:\\n{}\",\n            errors.join(\"\\n\")\n        )\n    }\n}\n\nasync fn github_review_bodies(\n    preferred_account: Option<&but_github::GithubAccountIdentifier>,\n    owner: &str,\n    repo: &str,\n    reviews: &[ForgeReviewUpdate],\n    storage: &but_forge_storage::Controller,\n) -> Result<(Vec<Option<Option<String>>>, Vec<String>)> {\n    let mut bodies = Vec::with_capacity(reviews.len());\n    let mut errors = Vec::new();\n    for review in reviews {\n        if review.update_description {\n            bodies.push(Some(review.body.clone()));","sourceCodeStart":2953,"sourceCodeEnd":2989,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-forge/src/review.rs#L2953-L2989","documentation":"After a failed push, the GitHub review code attempts to restore each PR's native stack membership (re-registering PRs into their stacks). Any error from those restore steps is collected; if any occurred, this error bails listing all of them. It indicates cleanup-after-failure also failed, so local and GitHub stack state may be inconsistent.","triggerScenarios":"A push of a stacked-review branch fails, then the restore_native_stack_membership routine errors on one or more PRs (API failures, permission problems, deleted PRs), producing this aggregated bail.","commonSituations":"GitHub API outages or rate limits during push failure recovery; token lacking rights to update the PRs; PRs closed/deleted concurrently so membership cannot be re-registered.","solutions":["Read the per-error lines in the message to identify which PRs failed to restore","Re-run the stack synchronization (but push / sync) once the API is reachable; the next successful sync recreates the desired stacks","Check the GitHub token's scopes and repo access for the failing PRs"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match push_result {\n    Err(e) if e.to_string().contains(\"Could not restore native GitHub stack membership\") => {\n        log::error!(\"push failed AND membership restore failed; state may be inconsistent: {e:#}\");\n        // schedule a re-sync\n    }\n    other => other?,\n}","preventionTips":["Ensure the token has rights to update all PRs in the stack before pushing","Monitor GitHub API health/rate limits around push operations","Re-run synchronization after this error to restore consistent state"],"tags":["github","api","stack","push","cleanup"],"backgroundTag":"api-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"}