gitbutlerapp/gitbutler · error

Can't receive a change without it's source or commit

Error message

Can't receive a change without it's source or commit

What it means

Thrown by UncommitDzHandler.ondrop in commitDropHandler.ts when a HunkDropDataV3 payload that is NOT uncommitted (i.e. it belongs to a commit) lacks data.stackId or data.commitId. Uncommitting a hunk requires knowing which stack and commit it is being moved out of, so the handler refuses to proceed.

Solutions

  1. Make sure the hunk drag source sets stackId and commitId whenever the hunk belongs to a commit (uncommitted=false)
  2. Check that the dropzone's accepts() rejects hunks without commitId/stackId so ondrop never sees them
  3. Refresh the commit list so ids reference live commits, not replaced ones
  4. In tests or programmatic drops, supply both stackId and commitId in HunkDropDataV3

Example fix

// before
const hunk = new HunkDropDataV3({ change, hunk, uncommitted: false, stackId: undefined, commitId: undefined });
// after
const hunk = new HunkDropDataV3({ change, hunk, uncommitted: false, stackId: sourceStack.id, commitId: commit.id });
Defensive patterns

Strategy: type-guard

Validate before calling

function canUncommitHunk(data: unknown): data is HunkDropDataV3 {
  return data instanceof HunkDropDataV3 &&
    !data.uncommitted && !!data.stackId && !!data.commitId;
}

Type guard

function hasHunkSource(d: HunkDropDataV3): d is HunkDropDataV3 & { stackId: string; commitId: string } {
  return !d.uncommitted && typeof d.stackId === 'string' && typeof d.commitId === 'string';
}

Try / catch

try {
  await handler.ondrop(hunkData);
} catch (e) {
  if (e instanceof Error && e.message.includes("without it's source or commit")) {
    toast.error('Cannot uncommit: hunk source stack/commit is unknown');
  } else throw e;
}

Prevention

When it happens

Trigger: Dropping a HunkDropDataV3 with uncommitted=false but undefined stackId or commitId onto the uncommit dropzone; the accepts() guard (which checks uncommitted, commitId and stackId) was bypassed or the payload was built incompletely.

Common situations: A hunk dragged from a stale commit list where the commit has since been replaced (replacedCommits) leaving dangling ids; hunk drop data constructed by tests or code paths that never set commitId; serialization losing stackId/commitId fields.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at apps/desktop/src/lib/dragging/dropHandlers/commitDropHandler.ts:275

								// Update the project state to point to the new commit if needed.
								updateUiState(this.uiState, stackId, commitId, workspace.replacedCommits);
							},
						);
					} else {
						throw new Error("Change drop data must specify the source stackId");
					}
					break;
				}
				case "branch":
					console.warn("Moving a branch into a commit is an invalid operation");
					break;
				case "worktree":
					console.warn("Moving a branch into a commit is an invalid operation");
					break;
			}
		} else {
			if (!(data.stackId && data.commitId)) {
				throw new Error("Can't receive a change without it's source or commit");
			}
			const previousPathBytes =
				data.change.status.type === "Rename" ? data.change.status.subject.previousPathBytes : null;

			const sourceStackId = data.stackId;
			const sourceCommitId = data.commitId;

			await withStackBusy(
				this.uiState,
				this.projectId,
				{ commitId: sourceCommitId, stackIds: [sourceStackId] },
				async () => {
					const { workspace } = await this.stackService.uncommitChanges({
						projectId: this.projectId,
						stackId: sourceStackId,
						commitId: sourceCommitId,
						changes: [
							{

View on GitHub (pinned to 58e5313667)