can1357/oh-my-pi · error · Error

git apply --3way failed for task ${taskId}: ${stderr}

Error message

git apply --3way failed for task ${taskId}: ${stderr}

What it means

When merging an isolated task's patch back into the parent worktree, the code first tries plain `git apply`, then retries with `git apply --3way`. If the three-way attempt also fails, it logs diagnostics (exit code, stderr, patch head) and throws with the git stderr embedded so the conflict cause is visible.

Source

Thrown at packages/coding-agent/src/task/worktree.ts:660

		try {
			await repo.applyPatch(patchText, { threeWay: true });
		} catch (err) {
			if (!vcs.isVcsError(err)) throw err;
			threeWayErr = err;
		}
		if (threeWayErr) {
			const wipPatches = collectWipPatches(baselineWip);
			if (wipPatches.length === 0 || !baselineWip) {
				const stderr = threeWayErr.stderr.slice(0, 2000);
				logger.error("commitToBranch: git apply --3way failed", {
					taskId,
					exitCode: threeWayErr.exitCode,
					stderr,
					initialStderr: plainErr.stderr.slice(0, 2000),
					patchSize: patchText.length,
					patchHead: patchText.slice(0, 500),
				});
				throw new Error(`git apply --3way failed for task ${taskId}: ${stderr}`);
			}
			try {
				// `git apply --3way` leaves conflict markers in `U` files when
				// it can't resolve; reset the worktree so the WIP-seeded retry
				// starts from a clean HEAD tree.
				await repo.reset("hard", "HEAD");
				await applyDeltaOverBaselineWip(tmpDir, taskId, patchText, wipPatches, baselineWip);
			} catch (wipErr) {
				if (!vcs.isVcsError(wipErr)) throw wipErr;
				const stderr = wipErr.stderr.slice(0, 2000);
				logger.error("commitToBranch: git apply with baseline WIP failed", {
					taskId,
					exitCode: wipErr.exitCode,
					stderr,
					threeWayStderr: threeWayErr.stderr.slice(0, 2000),
					initialStderr: plainErr.stderr.slice(0, 2000),
					patchSize: patchText.length,
					patchHead: patchText.slice(0, 500),

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the embedded stderr to find the conflicting file(s); manually apply the task's changes or rebase them
  2. Reduce conflict window: merge task results promptly, or re-run the task against the current parent HEAD
  3. If binary files conflict, apply those hunks manually
Defensive patterns

Strategy: try-catch

Validate before calling

// dry-run the patch before the real 3-way apply
const probe = await $`git apply --check --3way patch.diff`.cwd(repoDir).quiet().nothrow();
if (probe.exitCode !== 0) console.error(probe.stderr.text());

Try / catch

try {
	await mergeTaskPatch(taskId, patchText);
} catch (err) {
	if ((err as Error).message.startsWith("git apply --3way failed")) {
		// rebase task branch onto current HEAD and regenerate patch
	}
	throw err;
}

Prevention

When it happens

Trigger: Calling the patch-application path for a task whose diff cannot be applied even with 3-way merge: files changed in both the task worktree and the parent (real conflicts), missing context, or paths deleted/renamed in the parent since the task started.

Common situations: Long-running subagent tasks whose target files were edited in the main worktree meanwhile; binary files (git apply --3way can't merge binaries); patches produced against a stale baseline commit.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/8884b45a4b50a556. Report an issue: GitHub.