{"record":{"id":"21fa452288cb48a0","repo":"gitbutlerapp/gitbutler","slug":"s-does-not-name-an-uncommitted-change-or-branc","errorCode":null,"errorMessage":"'{s}' does not name an uncommitted change or branch; refusing to absorb everything","messagePattern":"'(.+?)' does not name an uncommitted change or branch; refusing to absorb everything","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but/src/command/legacy/absorb.rs","lineNumber":61,"sourceCode":"    let mut guard = ctx.exclusive_worktree_access();\n    let id_map = IdMap::new_from_context(ctx, guard.read_permission())?;\n    let source: Option<CliId> = source\n        .map(|s| -> anyhow::Result<CliId> {\n            // Uncommitted selectors resolve in the uncommitted namespace first\n            // so later commits cannot shadow them; branch selectors resolve in\n            // the full namespace. A selector that names neither is an error —\n            // silently falling back to absorbing everything is never intended.\n            let resolved =\n                match crate::id::parser::resolve_uncommitted_part(ctx, &id_map, s) {\n                    Ok(ids) if !ids.is_empty() => ids,\n                    _ => parse_sources(ctx, &id_map, s)?,\n                };\n            let mut acceptable = resolved.into_iter().filter(|id| {\n                matches!(id, CliId::UncommittedHunkOrFile { .. })\n                    || matches!(id, CliId::Branch(..))\n            });\n            let first = acceptable.next().ok_or_else(|| {\n                anyhow::anyhow!(\n                    \"'{s}' does not name an uncommitted change or branch; refusing to absorb everything\"\n                )\n            })?;\n            if acceptable.next().is_some() {\n                anyhow::bail!(\n                    \"'{s}' is ambiguous - it matches more than one uncommitted change. Use more characters to disambiguate.\"\n                );\n            }\n            Ok(first)\n        })\n        .transpose()?;\n\n    let target = if let Some(source) = source {\n        match source {\n            CliId::UncommittedHunkOrFile(UncommittedHunkOrFile { hunks, .. }) => {\n                // Absorb this particular file\n                AbsorptionTarget::Hunks {\n                    hunks: hunks.map(|id_and_hunk| id_and_hunk.hunk).into(),","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but/src/command/legacy/absorb.rs#L43-L79","documentation":"When `but absorb` is given an explicit selector, the CLI resolves it against both uncommitted changes (resolve_uncommitted_part) and branch sources (parse_sources), then filters to UncommittedHunkOrFile/Branch IDs. If nothing acceptable remains, this error is raised deliberately — the comment in the source states silently falling back to absorbing everything is never intended. It means the selector string matched no uncommitted change and no branch.","triggerScenarios":"Calling absorb with a stale/mistyped prefix whose change has since been committed or discarded; using a plain branch name when branches aren't in the ID map; passing a file-path fragment that matches no current worktree hunk; selectors for entries in a different workspace's ID map.","commonSituations":"Re-running an old command from shell history after the worktree changed; abbreviating an ID with too few characters so it matches nothing (note: matching more than one raises the separate ambiguity error); ID maps from `but id` being stale after commits.","solutions":["Run `but id` (or the equivalent listing) to refresh and see the currently valid selectors, then retry with one of them.","If the change was already committed, absorb is the wrong operation — target the commit/branch directly instead.","Use a longer, exact prefix of the listed ID or a path prefix that uniquely names a dirty file.","If you actually meant 'absorb everything', omit the selector entirely; the guard exists to prevent accidental mass-absorption."],"exampleFix":"# before: stale selector\n$ but absorb 3a\nError: '3a' does not name an uncommitted change or branch; refusing to absorb everything\n\n# after: look up a live ID and retry\n$ but id\n$ but absorb 3a9f","handlingStrategy":"validation","validationCode":"// Verify the selector resolves before running absorb\nlet id_map = IdMap::legacy_new_from_context(ctx)?;\nlet resolved = id_map.parse_using_context(selector, ctx)?;\nlet ok = resolved.iter().any(|id| matches!(id, CliId::UncommittedHunkOrFile { .. } | CliId::Branch(..)));\nanyhow::ensure!(ok, \"selector '{selector}' resolves to nothing absorbable\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Regenerate the ID map (`but id`) right before scripted absorb commands.","Never reuse selectors from old shell history across worktree mutations.","Use unique path prefixes for stability."],"tags":["absorb","selector","id-map","cli"],"backgroundTag":"unresolvable-id-selector","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}