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

Open 💬 1 comment Opened Apr 2, 2026 by 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)

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗