Skip to content

[Bug]: Codex streaming examples parse transport chunks as complete JSONL records #302

Description

@victorgulchenko

Sandbox ID or Build ID

Not applicable: documentation/source-level control, no sandbox call

Environment

Timestamp of the issue

2026-07-16 17:31 CEST

Frequency

Happens every time

Expected behavior

The Codex streaming examples should carry an incomplete trailing fragment across onStdout callbacks and call JSON.parse / json.loads only for complete newline-terminated JSONL records. A final non-empty remainder should be flushed or rejected when the command completes.

Actual behavior

Both current examples split and parse each callback value independently. The JavaScript SDK forwards each decoded transport data event directly to onStdout; it does not buffer until a newline, and its own unit test deliberately splits a UTF-8 character across two data events. A valid Codex JSONL record split at the same supported event boundary is therefore parsed while incomplete.

Controlled result:

Delivery Current JS example Remainder-buffered control
One complete line per callback 2 records 2 records
Same bytes in chunks of 19, 46, 74, and 3 characters SyntaxError: Unterminated string in JSON at position 19 2 records

Claim boundary: this is a deterministic documentation-parser failure, not a claim that the E2B sandbox or Codex process itself fails.

Issue reproduction

  1. Use current docs/agents/codex.mdx lines 201-247. The JavaScript handler is:
onStdout: (data) => {
  for (const line of data.split('\n').filter(Boolean)) {
    const event = JSON.parse(line)
    console.log(`[${event.type}]`, event)
  }
}
  1. Create two valid Codex-shaped JSONL records:
const records = [
  { type: 'thread.started', thread_id: 'emic-e2b-control' },
  { type: 'item.completed', item: { type: 'agent_message', text: 'refactor complete' } },
]
const jsonl = records.map(JSON.stringify).join('\n') + '\n'
  1. Call the documented handler with one complete JSONL line per callback. It parses 2 records.
  2. Call the same handler with the same bytes split at character positions 19, 65, and 139, representing supported transport/event boundaries. It throws on the first incomplete record.
  3. Run the same arbitrary chunks through a handler that preserves the trailing fragment between callbacks. It parses both records exactly once and in order.

Source confirmation: packages/js-sdk/src/sandbox/commands/commandHandle.ts dispatches each decoded data event directly to onStdout. packages/js-sdk/tests/sandbox/commands/commandHandle.test.ts, test decodes multibyte characters split across chunks, confirms event boundaries can split output bytes.

Additional context

Current source:

The Python example has the same structure with data.strip().split("\n") and json.loads(line), so it has the equivalent partial-record failure.

Suggested fix: keep a remainder buffer outside the callback, append each chunk, parse only complete newline-delimited records, and handle the final remainder after commands.run returns.

I searched this repository's issues and pull requests for the exact parser plus broader JSONL/streaming terms and found no matching report.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions