stablyai/orca · warning · Error

terminal_history_recovery_protected

Error message

terminal_history_recovery_protected

What it means

terminal_history_recovery_protected: openSession detected a '.unreadable-recovery' marker file in the session's history directory. This marker is written when a previous generation was quarantined as unreadable, and its presence blocks a new writer from attaching to potentially corrupt data. Opening is refused unless quarantineUnreadableRecovery is explicitly requested to re-quarantine.

Source

Thrown at src/main/daemon/history-manager.ts:64

  ) {
    this.onWriteError = opts?.onWriteError
    this.checkpointMaxBytes = opts?.checkpointMaxBytes ?? TERMINAL_HISTORY_CHECKPOINT_MAX_BYTES
    // Why: a quit between tombstone and reclaim leaves the tree on disk; nothing else rescans the queue.
    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))

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Pass quarantineUnreadableRecovery: true in OpenSessionOptions if you intend to re-quarantine and start clean.
  2. If the marker is stale and the directory is known-good, remove the .unreadable-recovery marker file manually (clearTerminalHistoryRecoveryProtection).
  3. Investigate why the marker was left behind (prior write failure / disk issue) before forcing removal.
  4. Treat history as best-effort: the session can still open without history rather than retrying indefinitely.

Example fix

// before: plain open on a protected dir
await history.openSession(id, { cwd, cols, rows })

// after: re-quarantine to start clean
await history.openSession(id, { cwd, cols, rows, quarantineUnreadableRecovery: true })
Defensive patterns

Strategy: fallback

Validate before calling

import { hasTerminalHistoryRecoveryProtection } from './terminal-history-recovery-quarantine'
if (hasTerminalHistoryRecoveryProtection(basePath, sessionId)) {
  // decide: re-quarantine (quarantineUnreadableRecovery: true) or open without history
}

Type guard

function isHistoryRecoveryProtected(e: unknown): boolean {
  return e instanceof Error && e.message === 'terminal_history_recovery_protected'
}

Try / catch

try {
  await history.openSession(id, { cwd, cols, rows })
} catch (e) {
  if (e instanceof Error && e.message === 'terminal_history_recovery_protected') {
    // re-quarantine and start clean
    await history.openSession(id, { cwd, cols, rows, quarantineUnreadableRecovery: true })
  } else { throw e }
}

Prevention

When it happens

Trigger: openSession on a sessionId whose history dir contains the RECOVERY_PROTECTION_MARKER, with opts.quarantineUnreadableRecovery falsy. Typically a prior recovery attempt marked the directory unreadable and a new open is racing in without the quarantine flag.

Common situations: A crashed/corrupt history directory left the protection marker after a failed quarantine; restarting a session whose prior generation was unreadable; an external tool partially clearing the recovery-quarantine dir but leaving the marker.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/b3353816edfe6438. Report an issue: GitHub.