{"record":{"id":"256b42c1d9945447","repo":"gitbutlerapp/gitbutler","slug":"branchnotfound","errorCode":"BranchNotFound","errorMessage":"branch with ID {stack_id} not found","messagePattern":"branch with ID (.+?) not found","errorType":"exception","errorClass":"but_error::Code","httpStatus":null,"severity":"error","filePath":"crates/but-api/src/legacy/virtual_branches.rs","lineNumber":311,"sourceCode":"    }\n\n    Ok(())\n}\n\nfn apply_stack_order_updates(\n    workspace: &mut but_core::ref_metadata::Workspace,\n    updates: Vec<BranchUpdateRequest>,\n) -> Result<bool> {\n    let mut requested_orders = HashMap::new();\n\n    for update in updates {\n        let stack_id = update.id.context(\"BUG(opt-stack-id)\")?;\n        if !workspace\n            .stacks\n            .iter()\n            .any(|stack| stack.id == stack_id && stack.is_in_workspace())\n        {\n            return Err(anyhow!(\"branch with ID {stack_id} not found\")\n                .context(but_error::Code::BranchNotFound));\n        }\n\n        if let Some(order) = update.order {\n            requested_orders.insert(stack_id, order);\n        }\n    }\n\n    if requested_orders.is_empty() {\n        return Ok(false);\n    }\n\n    let original_stack_ids = workspace\n        .stacks\n        .iter()\n        .map(|stack| stack.id)\n        .collect::<Vec<_>>();\n    let original_orders = original_stack_ids","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but-api/src/legacy/virtual_branches.rs#L293-L329","documentation":"While applying BranchUpdateRequests (e.g. stack reordering), every update must reference a stack that exists in the current workspace metadata and is_in_workspace() (applied). A request carrying the id of a deleted, unapplied, or unknown stack fails with Code::BranchNotFound before any order change is written.","triggerScenarios":"Sending stack order updates containing an id that was unapplied or deleted after the client fetched the list; two clients racing where one deletes a stack while the other reorders; SDK scripts reusing cached ids.","commonSituations":"Drag-to-reorder racing an unapply; frontend state holding stale stack ids; batch updates built from an old workspace snapshot.","solutions":["Refetch the current stacks and resend the update with live ids","Re-apply the stack if it should still be orderable","Filter out ids of stacks no longer in the workspace before sending the batch"],"exampleFix":"// before: batch built from a stale snapshot\nupdate_stack_orders(ctx, stale_updates)?;\n\n// after: intersect with live workspace stacks\nlet live: HashSet<StackId> = workspace_stacks(ctx)?.iter().filter_map(|s| s.id).collect();\nlet updates: Vec<_> = stale_updates.into_iter()\n    .filter(|u| u.id.map(|id| live.contains(&id)).unwrap_or(false))\n    .collect();\nif !updates.is_empty() { update_stack_orders(ctx, updates)?; }","handlingStrategy":"validation","validationCode":"let ws = current_workspace(ctx)?;\nlet live: HashSet<_> = ws.stacks.iter()\n    .filter(|s| s.is_in_workspace())\n    .filter_map(|s| s.id)\n    .collect();\nlet updates: Vec<_> = requested.into_iter()\n    .filter(|u| u.id.is_some_and(|id| live.contains(&id)))\n    .collect();\nif updates.is_empty() { return Ok(false); }","typeGuard":"use but_error::{AnyhowContextExt, Code};\n\nfn is_branch_not_found(err: &anyhow::Error) -> bool {\n    err.custom_context().is_some_and(|c| c.code == Code::BranchNotFound)\n}","tryCatchPattern":"match update_stack_orders(ctx, updates) {\n    Err(err) if is_branch_not_found(&err) => {\n        // refetch stacks and retry with refreshed ids; drop deleted ones\n    }\n    other => other,\n}","preventionTips":["Build reorder batches from the freshest workspace listing, never cached state","Invalidate client-side id caches after unapplying or deleting stacks","Re-fetch and retry on BranchNotFound instead of surfacing it to users"],"tags":["workspace","stacks","ordering","branch-not-found","stale-id"],"backgroundTag":"stale-entity-id","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}