stablyai/orca · error · Error

Too many pending terminal history seed transfers

Error message

Too many pending terminal history seed transfers

What it means

Thrown by TerminalHistorySeedTransferRegistry.start when the number of in-flight transfers has reached MAX_TRANSFERS (8). Each transfer holds chunks in memory until finish/take or a 30s TTL, so the registry caps concurrency to bound retained bytes. Starting a ninth transfer is refused.

Source

Thrown at src/main/daemon/terminal-history-seed-transfer-registry.ts:33

  utf8Bytes: number
  hash: ReturnType<typeof createHash>
  finished: boolean
  timer: ReturnType<typeof setTimeout>
}

export class TerminalHistorySeedTransferRegistry {
  private transfers = new Map<string, Transfer>()
  private retainedBytes = 0

  constructor(
    private readonly maxRetainedBytes = TERMINAL_HISTORY_CHECKPOINT_MAX_BYTES,
    private readonly transferTtlMs = TRANSFER_TTL_MS
  ) {}

  start(ownerId: string, manifest: TerminalHistorySeedTransferManifest): string {
    this.validateManifest(manifest)
    if (this.transfers.size >= MAX_TRANSFERS) {
      throw new Error('Too many pending terminal history seed transfers')
    }
    const transferId = randomUUID()
    const timer = setTimeout(() => this.delete(transferId), this.transferTtlMs)
    timer.unref()
    this.transfers.set(transferId, {
      ownerId,
      manifest: { ...manifest },
      chunks: [],
      codeUnits: 0,
      utf8Bytes: 0,
      hash: createHash('sha256'),
      finished: false,
      timer
    })
    return transferId
  }

  append(ownerId: string, transferId: string, index: number, data: string): void {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Complete (finish + take) or abort existing transfers before starting new ones; do not let transfers leak.
  2. Serialize seed transfers across sessions so you never exceed 8 in flight.
  3. Wait for the 30s TTL to reap stale transfers, then retry start.
  4. Call clearOwner(clientId) when a client disconnects to reclaim its transfers.

Example fix

// before: open a transfer per tab without bounding
for (const tab of tabs) {
  const id = registry.start(clientId, metrics)
}

// after: bound concurrency and complete each before the next
for (const batch of chunks(tabs, 8)) {
  const ids = batch.map(t => registry.start(clientId, measure(t)))
  // append + finish + take each, then proceed
}
Defensive patterns

Strategy: retry

Validate before calling

// Bound in-flight transfers before starting a new one (registry max is 8)
if (pendingTransferCount >= 8) {
  // wait for some to finish/expire, or serialize
}

Type guard

function isTooManySeedTransfers(e: unknown): boolean {
  return e instanceof Error && e.message === 'Too many pending terminal history seed transfers'
}

Try / catch

try {
  return registry.start(ownerId, manifest)
} catch (e) {
  if (e instanceof Error && e.message === 'Too many pending terminal history seed transfers') {
    await waitForTransferSlot(registry)  // complete/abort one, or wait ~TTL
    return registry.start(ownerId, manifest)
  }
  throw e
}

Prevention

When it happens

Trigger: start() called while transfers.size >= 8, i.e., 8 transfers are already pending (not yet finished, taken, aborted, or expired).

Common situations: Bulk-restoring many terminal sessions at once (e.g., after a crash restoring 9+ tabs) each starting a seed transfer; transfers leaked because finish/take was never called and the 30s TTL has not yet elapsed; a client bug opening transfers without completing them.

Related errors


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