stablyai/orca · error · NdjsonLineTooLongError

NDJSON line exceeds max ${maxLineBytes} bytes (${lineBytes}

Error message

NDJSON line exceeds max ${maxLineBytes} bytes (${lineBytes} bytes encoded)

What it means

NdjsonLineTooLongError: encodeNdjson throws when the JSON-serialized message exceeds the byte limit (default 16 MB, NDJSON_MAX_LINE_BYTES). This is a hard backstop so a daemon socket peer that never sends a newline — or a single oversized frame — cannot grow the parser buffer without bound. The error carries the actual and maximum byte counts.

Source

Thrown at src/main/daemon/ndjson.ts:17

export const NDJSON_MAX_LINE_BYTES = 16 * 1024 * 1024

export class NdjsonLineTooLongError extends Error {
  constructor(
    readonly lineBytes: number,
    readonly maxLineBytes: number
  ) {
    super(`NDJSON line exceeds max ${maxLineBytes} bytes (${lineBytes} bytes encoded)`)
    this.name = 'NdjsonLineTooLongError'
  }
}

export function encodeNdjson(msg: unknown, maxLineBytes = NDJSON_MAX_LINE_BYTES): string {
  const line = JSON.stringify(msg)
  const lineBytes = Buffer.byteLength(line, 'utf8')
  if (lineBytes > maxLineBytes) {
    throw new NdjsonLineTooLongError(lineBytes, maxLineBytes)
  }
  return `${line}\n`
}

export type NdjsonParser = {
  feed(chunk: string): void
  reset(): void
}

export type NdjsonParserOptions = {
  maxLineBytes?: number
}

export function createNdjsonParser(
  onMessage: (msg: unknown) => void,
  onError?: (err: Error) => void,
  options: NdjsonParserOptions = {}
): NdjsonParser {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Chunk the large payload into multiple smaller NDJSON messages (e.g., the history seed transfer protocol) instead of one frame.
  2. Raise maxLineBytes deliberately only if you control both peers and understand the memory cost.
  3. Strip/avoid embedding large binary blobs inline; reference them by transfer id instead.
  4. On the parser side, handle the onError callback to surface the oversized-line condition rather than silently dropping data.

Example fix

// before: one giant frame
encodeNdjson({ scrollback: hugeBase64 })

// after: chunked transfer
const metrics = measureTerminalHistorySeed([hugeBase64])
const id = registry.start(clientId, metrics)
for (const chunk of iterateTerminalHistorySeedChunks([hugeBase64])) {
  registry.append(clientId, id, i++, chunk)
}
Defensive patterns

Strategy: validation

Validate before calling

import { NDJSON_MAX_LINE_BYTES } from './ndjson'
function fitsNdjson(msg: unknown, max = NDJSON_MAX_LINE_BYTES): boolean {
  return Buffer.byteLength(JSON.stringify(msg), 'utf8') <= max
}
if (!fitsNdjson(msg)) { /* chunk the payload instead */ }

Type guard

import { NdjsonLineTooLongError } from './ndjson'
function isNdjsonTooLong(e: unknown): boolean {
  return e instanceof NdjsonLineTooLongError
}

Try / catch

try {
  sock.write(encodeNdjson(msg))
} catch (e) {
  if (e instanceof NdjsonLineTooLongError) {
    // split into chunked transfer messages
    sendChunked(sock, msg)
  } else { throw e }
}

Prevention

When it happens

Trigger: encodeNdjson(msg) where Buffer.byteLength(JSON.stringify(msg), 'utf8') exceeds maxLineBytes. The parser path (createNdjsonParser) reports the same message via onError when an incoming line exceeds the limit.

Common situations: Embedding a very large base64 blob, screenshot, or scrollback payload in a single NDJSON frame; a payload that grew past the limit after a format change; a peer streaming an unbounded paste without chunking.

Related errors


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