gitbutlerapp/gitbutler · error

Invalid source: expected an uncommitted file or branch

Error message

Invalid source: expected an uncommitted file or branch

What it means

`but absorb` resolves its source argument into an AbsorptionTarget (an uncommitted file assignment or a branch/lane). If the resolved value falls into no known arm of the match — i.e. the argument named something that is neither an uncommitted file nor a branch — the handler bails with this generic invalid-source error.

Solutions

  1. Check `but status` and pass an uncommitted file path or an existing branch name
  2. Commit or note that the file's changes are already absorbed if it is no longer uncommitted
  3. Spell the branch exactly (local branch names, not remotes/origin refs)

Example fix

// before
but absorb origin/feature-x   # remote ref, not a branch target
// after
but absorb feature-x
Defensive patterns

Strategy: validation

Validate before calling

function assertAbsorbableSource(name, uncommittedFiles, localBranches) {
  if (!uncommittedFiles.includes(name) && !localBranches.includes(name)) {
    throw new Error(`Invalid absorb source: ${name} is not an uncommitted file or branch`);
  }
}

Type guard

const isValidSource = (name, files, branches) => files.includes(name) || branches.includes(name);

Try / catch

try {
  await exec(`but absorb ${src}`);
} catch (e) {
  if (e.message.includes('Invalid source')) {
    console.error('Pick a name from but status (uncommitted file or branch)');
  } else throw e;
}

Prevention

When it happens

Trigger: Passing an argument to `but absorb` that resolves to something other than an uncommitted file or a branch: e.g. a committed-but-clean file path, a ref that is not a local branch, an empty selection enum arm, or a stale name for a file whose changes were already committed/stashed.

Common situations: Naming a file whose changes were already committed (no longer uncommitted); typos in branch names; trying to absorb against a remote-only ref; running absorb after the working tree changed so a previously valid name no longer resolves.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/7e7e22788871100c. Report an issue: GitHub.

Appendix: source

Thrown at crates/but/src/command/legacy/absorb.rs:89

        })
        .transpose()?;

    let target = if let Some(source) = source {
        match source {
            CliId::UncommittedHunkOrFile(UncommittedHunkOrFile { hunks, .. }) => {
                // Absorb this particular file
                AbsorptionTarget::Hunks {
                    hunks: hunks.map(|id_and_hunk| id_and_hunk.hunk).into(),
                }
            }
            CliId::Branch(branch) => {
                // Absorb everything that is assigned to this lane
                AbsorptionTarget::Branch {
                    branch_name: branch.name,
                }
            }
            _ => {
                anyhow::bail!("Invalid source: expected an uncommitted file or branch");
            }
        }
    } else {
        // Try to absorb everything uncommitted
        Default::default()
    };

    // TODO: Ideally, there's a simpler way of getting the worktree changes without passing the context to it.
    // At this time, the context is passed pretty deep into the function.
    let absorption_plan =
        but_api::legacy::absorb::absorption_plan_with_perm(ctx, target, guard.write_permission())?;

    // Absorbing into a landed commit entangles the new change with an entire
    // already-merged commit, which conflicts on the next `but pull`. Drop such
    // plan entries and tell the user, instead of silently amending them.
    let merged = MergedUpstream::from_ctx(ctx, allow_merged)?;
    let Some((absorption_plan, skipped_merged)) =
        drop_landed_absorptions(absorption_plan, &merged, out)?

View on GitHub (pinned to 58e5313667)