{"record":{"id":"8beead263627b00b","repo":"gitbutlerapp/gitbutler","slug":"no-uncommitted-changes-found-for-the-selected-file","errorCode":null,"errorMessage":"No uncommitted changes found for the selected files","messagePattern":"No uncommitted changes found for the selected files","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-api/src/legacy/absorb.rs","lineNumber":188,"sourceCode":"                true,\n                perm.read_permission(),\n            )?;\n            let all_assignments = worktree_changes.assignments;\n            let dependencies = worktree_changes.dependencies;\n\n            // Include hunks that are unassigned or assigned to the acting stack,\n            // so that dependency locks can route unassigned hunks correctly.\n            let candidates: Vec<AbsorbCandidate> = all_assignments\n                .into_iter()\n                .filter(|a| {\n                    changes.iter().any(|c| c.path_bytes == a.path_bytes)\n                        && (a.stack_id.is_none() || a.stack_id == assigned_stack_id)\n                })\n                .map(Into::into)\n                .collect();\n\n            if candidates.is_empty() {\n                anyhow::bail!(\"No uncommitted changes found for the selected files\");\n            }\n\n            (candidates, dependencies)\n        }\n        AbsorptionTarget::Hunks { hunks } => {\n            // Compute hunk dependencies only for this target since changes_in_worktree isn't called\n            let (repo, ws, _db) = ctx.workspace_and_db_with_perm(perm.read_permission())?;\n            let dependencies =\n                hunk_dependencies_for_workspace_changes_by_worktree_dir(&repo, &ws, None).ok();\n            drop((repo, ws, _db));\n            (hunks.into_iter().map(Into::into).collect(), dependencies)\n        }\n        AbsorptionTarget::All => {\n            // Get all worktree changes, assignments, and dependencies\n            // TODO: Ideally, there's a simpler way of getting the worktree changes without passing the context to it.\n            // At this time, the context is passed pretty deep into the function.\n            let worktree_changes = crate::diff::changes_in_worktree_with_perm(\n                ctx,","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-api/src/legacy/absorb.rs#L170-L206","documentation":"`absorption_plan_with_perm` throws this when the caller selected specific files for absorption but, after filtering the worktree changes to those files (and the relevant stack assignment), no eligible uncommitted changes remain. Nothing matches the file selection, so there is nothing to absorb and the operation bails.","triggerScenarios":"Calling `absorption_plan` with an `AbsorptionTarget::TreeChanges { changes, .. }` (or selected-file target) where the filter `a.stack_id.is_none() || a.stack_id == assigned_stack_id` over the requested files yields an empty candidate list — e.g. the files were already committed, are unmodified, or their changes belong to a different stack.","commonSituations":"Stale UI file list — the user committed the changes before hitting absorb; file paths that are actually clean in the worktree; changes assigned to a different branch than the target stack; path typos in the selection.","solutions":["Refresh the worktree status (`but status`) and re-select only files that currently have uncommitted changes.","Confirm the selected files' changes are assigned to (or unassigned from) the target stack, and reassign if needed.","If the changes were already committed, use commit-amend/squash operations instead of absorb."],"exampleFix":"// before\nabsorb(target, [\"src/clean_file.rs\"])  // Err: No uncommitted changes found for the selected files\n// after\nlet dirty = status().changes.filter(|c| c.uncommitted);\nabsorb(target, dirty.map(|c| c.path))","handlingStrategy":"validation","validationCode":"// filter the selection to files that actually have uncommitted changes\nconst status = await api.workspaceState();\nconst dirtyPaths = new Set(status.fileChanges.filter(f => f.uncommitted).map(f => f.path));\nconst eligible = selectedFiles.filter(p => dirtyPaths.has(p));\nif (!eligible.length) throw new Error('Selected files have no uncommitted changes; refresh selection');","typeGuard":"function filesHaveChanges(paths, fileChanges) {\n  const dirty = new Set(fileChanges.filter(f => f.uncommitted).map(f => f.path));\n  return paths.some(p => dirty.has(p));\n}","tryCatchPattern":"try {\n  await api.absorb({ files: selectedFiles });\n} catch (e) {\n  if (String(e).includes('No uncommitted changes found for the selected files')) {\n    const fresh = await refreshStatusAndReselect(); // re-query worktree state\n    if (fresh.length) await api.absorb({ files: fresh });\n  } else throw e;\n}","preventionTips":["Always re-query worktree status immediately before building the absorb selection.","Drop already-committed or clean files from the selection instead of passing stale lists.","Verify assignment (or unassigned status) matches the target stack for each selected file."],"tags":["git","uncommitted-changes","absorb","file-selection"],"backgroundTag":"empty-result-set","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"}