{"record":{"id":"ef355d1e2804de36","repo":"stablyai/orca","slug":"terminal-history-seed-transfer-digest-mismatch","errorCode":null,"errorMessage":"Terminal history seed transfer digest mismatch","messagePattern":"Terminal history seed transfer digest mismatch","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/daemon/terminal-history-seed-transfer-registry.ts","lineNumber":91,"sourceCode":"    this.retainedBytes += utf8Bytes\n    this.refreshExpiry(transferId, transfer)\n  }\n\n  finish(ownerId: string, transferId: string): void {\n    const transfer = this.getOwned(ownerId, transferId)\n    if (transfer.finished) {\n      throw new Error('Terminal history seed transfer is already finished')\n    }\n    if (\n      transfer.chunks.length !== transfer.manifest.chunkCount ||\n      transfer.codeUnits !== transfer.manifest.codeUnits\n    ) {\n      throw new Error('Terminal history seed transfer is incomplete')\n    }\n    const digest = transfer.hash.digest('hex')\n    if (digest !== transfer.manifest.sha256) {\n      this.delete(transferId)\n      throw new Error('Terminal history seed transfer digest mismatch')\n    }\n    transfer.finished = true\n    this.refreshExpiry(transferId, transfer)\n  }\n\n  take(ownerId: string, transferId: string): readonly string[] {\n    const transfer = this.getOwned(ownerId, transferId)\n    if (!transfer.finished) {\n      throw new Error('Terminal history seed transfer is not finished')\n    }\n    const chunks = transfer.chunks\n    this.delete(transferId)\n    return chunks\n  }\n\n  abort(ownerId: string, transferId: string): void {\n    this.getOwned(ownerId, transferId)\n    this.delete(transferId)","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/daemon/terminal-history-seed-transfer-registry.ts#L73-L109","documentation":"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.","triggerScenarios":"finish(ownerId, transferId) where transfer.hash.digest('hex') !== manifest.sha256. The chunkCount and codeUnits checks passed but the content digest differs.","commonSituations":"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.","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)."],"exampleFix":"// before: manifest sha256 computed over utf8 bytes (wrong)\nconst sha256 = createHash('sha256').update(Buffer.from(seed, 'utf8')).digest('hex')\n\n// after: match the registry's utf16le chunk hashing\nconst { sha256 } = measureTerminalHistorySeed([seed])","handlingStrategy":"fallback","validationCode":"import { measureTerminalHistorySeed } from './terminal-history-seed-chunks'\n// Compute the manifest the same way the registry hashes (utf16le per chunk)\nconst { sha256, codeUnits, chunkCount } = measureTerminalHistorySeed(segments)\n// Re-verify locally before finish\nlet h = createHash('sha256')\nfor (const chunk of sentChunks) h.update(Buffer.from(chunk, 'utf16le'))\nconst ok = h.digest('hex') === sha256","typeGuard":"function isSeedDigestMismatch(e: unknown): boolean {\n  return e instanceof Error && e.message === 'Terminal history seed transfer digest mismatch'\n}","tryCatchPattern":"try {\n  registry.finish(ownerId, transferId)\n} catch (e) {\n  if (e instanceof Error && e.message === 'Terminal history seed transfer digest mismatch') {\n    // transfer is deleted; fall back to opening the session without a history seed\n    await openSessionWithoutSeed(sessionId)\n  } else { throw e }\n}","preventionTips":["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)."],"tags":["history","seed-transfer","integrity","digest"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}