gitbutlerapp/gitbutler · error

BUG: a change from was pushed into a builder reading

Error message

BUG: a change from {} was pushed into a builder reading {}

What it means

Internal invariant violation: an `UncommittedChanges` from a change source that is neither equal to the builder's source nor a named worktree was pushed into the builder. This indicates a bug in how the change was resolved or routed (sources should either match or be a worktree handled by the earlier branch).

Solutions

  1. Report this as a bug to the GitButler project with the command and reproduction steps
  2. Verify ids come from the current `but status` output of the same workspace (stale output can misattribute sources)
  3. Retry after refreshing resolution state; if reproducible, include `but` version in the report
Defensive patterns

Strategy: try-catch

Validate before calling

debug_assert_eq!(uncommitted.source, builder.source, "mismatched change source pushed into diff-specs builder");

Try / catch

if let Err(e) = result {
    if e.to_string().starts_with("BUG: a change from") {
        // collect diagnostics and report upstream; this is an internal invariant violation
        report_bug(e);
    } else { return Err(e.into()); }
}

Prevention

When it happens

Trigger: `push_changes_from_uncommitted` sees `uncommitted.source != self.source` and `uncommitted.source.worktree_name()` returns None — i.e. a mismatched non-worktree source reached the builder, e.g. via `push_changes_from_id` with a wrongly attributed uncommitted id.

Common situations: Should not occur in normal use; encountered when a resolver returns uncommitted changes attributed to an unexpected source kind, or after internal refactors that change source tagging.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at crates/but/src/utils/diff_specs.rs:114

                anyhow::bail!("Cannot compute diff specs for committed hunks")
            }
        }
    }

    pub fn push_changes_from_uncommitted(
        &mut self,
        uncommitted: &UncommittedHunkOrFile,
    ) -> anyhow::Result<()> {
        // Specs are built against one checkout's repository, so a hunk from
        // another would silently address the wrong files. Operations that support
        // worktree sources validate the selection and construct the builder via
        // [`Self::for_change_source`]; the rest read the main worktree and refuse
        // worktree hunks here.
        if uncommitted.source != self.source {
            if let Some(name) = uncommitted.source.worktree_name() {
                anyhow::bail!("Cannot operate on uncommitted changes in worktree {name} yet");
            }
            anyhow::bail!(
                "BUG: a change from {} was pushed into a builder reading {}",
                uncommitted.source.describe(),
                self.source.describe()
            );
        }
        let hunks = uncommitted.hunks.iter().cloned();
        self.push_hunks(hunks.map(|id_and_hunk| id_and_hunk.hunk))
    }

    pub fn push_changes_from_path_prefix(
        &mut self,
        hunks: &nonempty::NonEmpty<IdAndHunk>,
    ) -> anyhow::Result<()> {
        self.push_hunks(hunks.iter().map(|id_and_hunk| id_and_hunk.hunk.clone()))
    }

    pub fn push_changes_from_committed_file(
        &mut self,

View on GitHub (pinned to 58e5313667)