stablyai/orca · warning · Error
terminal_history_recovery_generation_changed
Error message
terminal_history_recovery_generation_changed
What it means
terminal_history_recovery_generation_changed in openSession: the current fingerprint of the session history directory does not match the fingerprint captured at freeze time. The directory tree changed between freeze and open (concurrent write, external modification, or corruption), so opening would mix generations. The freeze is abandoned and the session's history is disabled.
Source
Thrown at src/main/daemon/history-manager.ts:68
schedulePendingSessionTreeRemovals(this.basePath)
}
async openSession(sessionId: string, opts: OpenSessionOptions): Promise<void> {
let recoveryFreeze = opts.recoveryFreeze
try {
this.disabledSessions.delete(sessionId)
const dir = join(this.basePath, getHistorySessionDirName(sessionId))
recoveryFreeze ??= await this.freezeForRecovery(sessionId)
const activeFreeze = this.requireRecoveryFreeze(sessionId, recoveryFreeze)
if (opts.quarantineUnreadableRecovery) {
quarantineTerminalHistorySession(this.basePath, sessionId, activeFreeze.fingerprint ?? null)
} else if (hasTerminalHistoryRecoveryProtection(this.basePath, sessionId)) {
throw new Error('terminal_history_recovery_protected')
} else if (
fingerprintTerminalHistorySession(this.basePath, sessionId) !== activeFreeze.fingerprint
) {
throw new Error('terminal_history_recovery_generation_changed')
}
this.recoveryFreezes.delete(sessionId)
mkdirSync(dir, { recursive: true })
const meta: SessionMeta = {
cwd: opts.cwd,
cols: opts.cols,
rows: opts.rows,
startedAt: new Date().toISOString(),
endedAt: null,
exitCode: null
}
writeFileSync(join(dir, 'meta.json'), JSON.stringify(meta, null, 2))
if (!opts.quarantineUnreadableRecovery) {
// Why: a crash before the first checkpoint must not replay a cleanly ended prior session.
for (const staleFile of [
join(dir, 'checkpoint.json'),View on GitHub (pinned to 1136503c6a)
Solutions
- Re-freeze and re-open once the directory is quiescent (no concurrent writers).
- If quarantine is appropriate, pass quarantineUnreadableRecovery to quarantine the changed generation and start fresh.
- Ensure no other process touches the history base path during recovery (disable sync/AV on that dir).
- Accept that history is disabled for this session and continue without it.
Defensive patterns
Strategy: fallback
Type guard
function isRecoveryGenerationChanged(e: unknown): boolean {
return e instanceof Error && e.message === 'terminal_history_recovery_generation_changed'
} Try / catch
try {
await history.openSession(id, opts)
} catch (e) {
if (e instanceof Error && e.message === 'terminal_history_recovery_generation_changed') {
// history disabled internally; continue without it, or re-quarantine
await history.openSession(id, { ...opts, quarantineUnreadableRecovery: true })
} else { throw e }
} Prevention
- Freeze and open within the same quiet window so the directory does not change generation.
- Keep concurrent writers/checkpoints off the session dir during recovery.
- Exclude the history base path from sync/AV tools that alter mtimes.
When it happens
Trigger: openSession where fingerprintTerminalHistorySession(basePath, sessionId) differs from activeFreeze.fingerprint, with no quarantine requested and no protection marker. The dir was modified after the recovery freeze was taken.
Common situations: Another process (or a leftover writer) modified the history directory between freezeForRecovery and openSession; filesystem metadata changed (inode/mtime) due to AV scanning or a sync tool; a crash left a partially-written directory.
Related errors
- terminal_history_recovery_protected
- terminal_history_recovery_generation_changed
- Artifact create recovery record does not match its storage i
- Terminal history seed transfer digest mismatch
- Refusing to patch unexpected node-pty console-list agent sou
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/19b53885bd9a91ad.
Report an issue: GitHub.