Yeachan-Heo/oh-my-codex · critical · Error

Native hook transaction failed (${message}) and rollback fai

Error message

Native hook transaction failed (${message}) and rollback failed; manual recovery is required (${rollbackFailures.join("; ")}).

What it means

When a native hook transaction fails, setup attempts full rollback. If any rollback step also fails, the collected rollbackFailures are joined with the original error so the user knows the system is left in a partially applied state requiring manual recovery.

Source

Thrown at src/cli/setup.ts:2137

			try {
				await assertNativeHookTransactionRollbackState(applied);
				await cleanupNativeHookTransactionStagedDeletions(
					restored,
					ancestorPrecondition,
					tracker,
					undefined,
					applied,
				);
				await assertNativeHookTransactionRollbackState(applied);
			} catch (cleanupError) {
				rollbackFailures.push(
					`staged deletion cleanup: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`,
				);
			}
		}
		const message = error instanceof Error ? error.message : String(error);
		if (rollbackFailures.length > 0) {
			throw new Error(
				`Native hook transaction failed (${message}) and rollback failed; manual recovery is required (${rollbackFailures.join("; ")}).`,
			);
		}
		throw new Error(`Native hook transaction failed and was rolled back: ${message}`);
	}
}


async function ensureBackup(
	destinationPath: string,
	contentChanged: boolean,
	backupContext: SetupBackupContext,
	options: Pick<SetupOptions, "dryRun" | "verbose">,
): Promise<boolean> {
	if (!contentChanged || !existsSync(destinationPath)) return false;

	const relativePath = relative(backupContext.baseRoot, destinationPath);
	const safeRelativePath =

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Parse the semicolon-joined rollbackFailures list to enumerate every file needing manual attention
  2. Manually restore each affected hooks.json/config.toml from the backup copies under the backup root
  3. Fix environmental causes (permissions, disk space, concurrent processes)
  4. Re-run setup once files are reconciled
Defensive patterns

Strategy: try-catch

Try / catch

try { await tx.apply(); } catch (e) { if (e instanceof Error && e.message.includes("rollback failed; manual recovery is required")) { const failures = e.message.match(/\((.*)\)\.?$/)?.[1]?.split("; ") ?? []; for (const f of failures) await recoverManually(f); } else throw e; }

Prevention

When it happens

Trigger: An apply-phase error (e.g. 380/381/383-style failures) followed by at least one failed rollback step — restore errors, claim errors, or staged-deletion cleanup errors recorded in rollbackFailures.

Common situations: Permission or disk problems affecting both apply and rollback, concurrent interference during the whole transaction window, or multi-point fault injection in tests.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/8dd92b934fc6c316. Report an issue: GitHub.