Skip to content

codex-app-server-sdk: StdioTransport crashes on prompts >64 KiB due to Python asyncio StreamReader limit #16554

@thomast8

Description

@thomast8

Summary

The codex-app-server-sdk Python package crashes with CodexTransportError: failed reading from stdio transport when a single JSON-RPC message (typically turn/start with a large prompt) exceeds ~65,536 bytes.

Root Cause

StdioTransport.connect() spawns the codex app-server subprocess via asyncio.create_subprocess_exec() without specifying the limit parameter:

# codex_app_server_sdk/transport.py, line 70-78
self._proc = await asyncio.wait_for(
    asyncio.create_subprocess_exec(
        *self._command,
        stdin=asyncio.subprocess.PIPE,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.DEVNULL,
        ...
    ),
    timeout=self._connect_timeout,
)

Python's asyncio.StreamReader defaults to a 64 KiB line limit (asyncio.streams._DEFAULT_LIMIT = 65536). The Codex app-server echoes input content in its JSON-RPC response lines on stdout. When a response line exceeds 64 KiB, StreamReader.readline() raises LimitOverrunError, which StdioTransport.recv() catches and re-raises as CodexTransportError("failed reading from stdio transport").

The app-server itself handles large prompts fine (verified by communicating with it directly).

Reproduction

import asyncio
from codex_app_server_sdk import CodexClient, ThreadConfig, TurnOverrides

async def main():
    client = CodexClient.connect_stdio(inactivity_timeout=30.0)
    await client.start()

    # This prompt produces a turn/start message >65536 bytes
    prompt = "Reply with 'ok'. " + ("x" * 80000)
    async for step in client.chat(
        prompt,
        thread_config=ThreadConfig(
            model="gpt-5.4-mini",
            base_instructions="Reply ok.",
            sandbox="read-only",
            approval_policy="never",
        ),
    ):
        print(step.text)

asyncio.run(main())
# -> CodexTransportError: failed reading from stdio transport

Fix

Pass limit=1_048_576 (1 MB) to asyncio.create_subprocess_exec() in StdioTransport.connect():

self._proc = await asyncio.wait_for(
    asyncio.create_subprocess_exec(
        *self._command,
        stdin=asyncio.subprocess.PIPE,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.DEVNULL,
        cwd=self._cwd,
        env=self._env,
        limit=1_048_576,  # 1 MB instead of default 64 KiB
    ),
    timeout=self._connect_timeout,
)

Verified working with prompts up to 200K characters after this change.

Environment

  • codex-app-server-sdk installed via pip
  • codex-cli 0.118.0
  • Python 3.13
  • macOS (Darwin 25.3.0)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsdkIssues related to the Codex SDK

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions