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
- Parse the semicolon-joined rollbackFailures list to enumerate every file needing manual attention
- Manually restore each affected hooks.json/config.toml from the backup copies under the backup root
- Fix environmental causes (permissions, disk space, concurrent processes)
- 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
- Guarantee permissions/disk health before transactions
- Prevent concurrent access to hook files during setup
- Keep the backup root intact — it is the recovery source
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
- Native hook transaction rollback removal claim failed (${err
- Native hook transaction rollback preserved ${artifact.path}
- Native hook transaction rollback preserved ${artifact.path}
- Native hook transaction failed and was rolled back: ${messag
- preLaunch ${completion.operation} failed
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/8dd92b934fc6c316.
Report an issue: GitHub.