stablyai/orca · error · Error
Terminal history seed transfer digest mismatch
Error message
Terminal history seed transfer digest mismatch
What it means
Thrown by TerminalHistorySeedTransferRegistry.finish when the SHA-256 of the received chunks (hashed as UTF-16LE, matching the manifest's measurement) does not equal manifest.sha256. The transfer is deleted and finish is rejected: the seed data was corrupted, truncated, reordered, or the manifest was computed differently than the chunks were sent. The mismatch is fatal because replaying corrupt scrollback would garble the terminal.
Source
Thrown at src/main/daemon/terminal-history-seed-transfer-registry.ts:91
this.retainedBytes += utf8Bytes
this.refreshExpiry(transferId, transfer)
}
finish(ownerId: string, transferId: string): void {
const transfer = this.getOwned(ownerId, transferId)
if (transfer.finished) {
throw new Error('Terminal history seed transfer is already finished')
}
if (
transfer.chunks.length !== transfer.manifest.chunkCount ||
transfer.codeUnits !== transfer.manifest.codeUnits
) {
throw new Error('Terminal history seed transfer is incomplete')
}
const digest = transfer.hash.digest('hex')
if (digest !== transfer.manifest.sha256) {
this.delete(transferId)
throw new Error('Terminal history seed transfer digest mismatch')
}
transfer.finished = true
this.refreshExpiry(transferId, transfer)
}
take(ownerId: string, transferId: string): readonly string[] {
const transfer = this.getOwned(ownerId, transferId)
if (!transfer.finished) {
throw new Error('Terminal history seed transfer is not finished')
}
const chunks = transfer.chunks
this.delete(transferId)
return chunks
}
abort(ownerId: string, transferId: string): void {
this.getOwned(ownerId, transferId)
this.delete(transferId)View on GitHub (pinned to 1136503c6a)
Solutions
- Recompute the manifest with measureTerminalHistorySeed (which hashes UTF-16LE over the exact chunk boundaries) and resend.
- Ensure chunks are appended in index order and not mutated between measure and send.
- Abort the failed transfer and fall back to opening the session without a history seed.
- Verify the transport is not altering payload encoding (e.g., a JSON layer re-encoding strings).
Example fix
// before: manifest sha256 computed over utf8 bytes (wrong)
const sha256 = createHash('sha256').update(Buffer.from(seed, 'utf8')).digest('hex')
// after: match the registry's utf16le chunk hashing
const { sha256 } = measureTerminalHistorySeed([seed]) Defensive patterns
Strategy: fallback
Validate before calling
import { measureTerminalHistorySeed } from './terminal-history-seed-chunks'
// Compute the manifest the same way the registry hashes (utf16le per chunk)
const { sha256, codeUnits, chunkCount } = measureTerminalHistorySeed(segments)
// Re-verify locally before finish
let h = createHash('sha256')
for (const chunk of sentChunks) h.update(Buffer.from(chunk, 'utf16le'))
const ok = h.digest('hex') === sha256 Type guard
function isSeedDigestMismatch(e: unknown): boolean {
return e instanceof Error && e.message === 'Terminal history seed transfer digest mismatch'
} Try / catch
try {
registry.finish(ownerId, transferId)
} catch (e) {
if (e instanceof Error && e.message === 'Terminal history seed transfer digest mismatch') {
// transfer is deleted; fall back to opening the session without a history seed
await openSessionWithoutSeed(sessionId)
} else { throw e }
} Prevention
- Compute the manifest with measureTerminalHistorySeed (UTF-16LE hashing, exact chunk boundaries).
- Send chunks in index order without mutation between measure and send.
- Verify the transport does not re-encode payload strings (e.g., JSON UTF-8 round-trips).
When it happens
Trigger: finish(ownerId, transferId) where transfer.hash.digest('hex') !== manifest.sha256. The chunkCount and codeUnits checks passed but the content digest differs.
Common situations: A chunk was corrupted in transit or reordered; the manifest's sha256 was computed over UTF-8 bytes or a different chunk boundary than the registry uses (UTF-16LE); a client re-chunked between measure and send; partial transfer where a chunk was dropped but sizes coincidentally summed correctly.
Related errors
- terminal_history_recovery_protected
- terminal_history_recovery_generation_changed
- terminal_history_recovery_generation_changed
- Too many pending terminal history seed transfers
- Terminal history seed transfer exceeds retained byte limit
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/ef355d1e2804de36.
Report an issue: GitHub.