Python SDK: CodexClient.close() leaves the app-server's stdout/stderr pipes open (and never waits after kill)
Los mantenedores suelen responder en 1 día
@akhil9tiet ya está trabajando en esto.
Desde el 17/10/2026.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 82/100
Línea de trabajo
Empieza por CodexClient.close() en sdk/python/src/openai_codex/client.py y luego inspecciona start() y la ruta de cierre síncrona utilizada por los clientes asíncronos. Usa el subproceso de sustitución launch_args_override proporcionado con python -X dev y ejecuta las pruebas pertinentes del SDK tratando las ResourceWarnings como errores. Terminar significa que el subproceso ha sido recolectado, los hilos del reader se han detenido, las tres tuberías están cerradas y no queda ninguna ResourceWarning.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
What issue are you seeing?
In the Python SDK, CodexClient.close() (sdk/python/src/openai_codex/client.py, lines 283–302 on main) never closes the app-server child process's stdout and stderr pipes. It closes stdin, terminates the process and joins the reader threads, but the two subprocess.PIPE file objects that start() opened (lines 268–278) stay open. They are only finalized whenever the Popen object is garbage-collected, and CPython reports each one:
ResourceWarning: unclosed file <_io.TextIOWrapper name=5 encoding='utf-8'>
ResourceWarning: unclosed file <_io.TextIOWrapper name=4 encoding='utf-8'>
AsyncCodex.close() / AsyncCodexClient.close() delegate to the same method, so every client the SDK creates leaves two handles to the garbage collector. A test suite that runs with -W error::ResourceWarning, or filterwarnings = error, fails on any test that closes a client. A long-running host that opens and closes many clients holds those handles until collection. On interpreters without prompt refcounting (PyPy), or when a reference cycle keeps the Popen alive, they can pile up.
A second, related gap, found by reading the code but not reproduced: if proc.wait(timeout=2) raises (for example subprocess.TimeoutExpired because the app-server doesn't exit within 2 s of SIGTERM on POSIX), close() calls proc.kill() but never waits for the process afterwards. On POSIX that leaves a zombie until the Popen is collected, and Popen.__del__ then warns ResourceWarning: subprocess <pid> is still running. The bare except Exception: also hides any other failure from terminate() / wait().
What steps can reproduce the bug?
This needs no Codex installation or sign-in. launch_args_override starts a stand-in process that just waits on stdin, which is enough because close() alone shows the problem.
"""CodexClient.close() leaves the app-server's stdout and stderr pipes open.
Needs no Codex install or sign-in: the launch override starts a stand-in process that waits on stdin.
Run with: python -X dev repro_close_leaks_pipes.py
"""
import gc
import sys
import warnings
import openai_codex
from openai_codex.client import CodexClient, CodexConfig
warnings.simplefilter("always", ResourceWarning)
print(f"openai-codex {openai_codex.__version__ if hasattr(openai_codex, '__version__') else '?'}, Python {sys.version.split()[0]}, {sys.platform}")
client = CodexClient(CodexConfig(launch_args_override=(sys.executable, "-c", "import sys; sys.stdin.read()")))
client.start()
proc = client._proc # kept only to show the pipes' state after close()
client.close()
print(f"after close(): returncode={proc.returncode}, stdin.closed={proc.stdin.closed}, stdout.closed={proc.stdout.closed}, stderr.closed={proc.stderr.closed}")
del proc, client
gc.collect()
print("done")
Output on Windows 11 with Python 3.14.2, identical for openai-codex 0.162.1 and 0.163.0 (latest on PyPI at the time of writing), shown here for 0.163.0 (stderr warnings first):
repro_close_leaks_pipes.py:25: ResourceWarning: unclosed file <_io.TextIOWrapper name=5 encoding='utf-8'>
del proc, client
ResourceWarning: Enable tracemalloc to get the object allocation traceback
repro_close_leaks_pipes.py:25: ResourceWarning: unclosed file <_io.TextIOWrapper name=4 encoding='utf-8'>
del proc, client
ResourceWarning: Enable tracemalloc to get the object allocation traceback
openai-codex 0.163.0, Python 3.14.2, win32
after close(): returncode=1, stdin.closed=True, stdout.closed=False, stderr.closed=False
done
AsyncCodex and AsyncCodexClient close through this same method. We first saw the warnings in an acceptance suite that runs the production async client against a mediated app-server process.
What is the expected behavior?
After close() returns:
- The child process has exited and been waited for (reaped), including on the
kill()path. - The reader threads have finished.
- stdin, stdout and stderr are all closed.
- No
ResourceWarningis emitted when the client is garbage-collected.
A possible fix in CodexClient.close():
if proc.stdin:
proc.stdin.close()
try:
proc.terminate()
proc.wait(timeout=2)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait()
# The process has exited, so both reader threads reach EOF; join them before closing the pipes they read.
if self._stderr_thread and self._stderr_thread.is_alive():
self._stderr_thread.join(timeout=0.5)
if self._reader_thread and self._reader_thread.is_alive():
self._reader_thread.join(timeout=0.5)
for stream in (proc.stdout, proc.stderr):
if stream is not None:
stream.close()
Closing the pipes after the threads are joined avoids closing a file object another thread is still iterating. Catching subprocess.TimeoutExpired instead of Exception keeps unexpected failures visible. Alternatively, with proc: around the teardown closes all three pipes and waits.
Additional information
- Package:
openai-codex0.162.1 and 0.163.0 (sdk/pythonin this repo). The code is unchanged onmain. - Python 3.14.2 on Windows 11. The pipe leak is platform-independent. The missing wait after
kill()matters mainly on POSIX. - This is separate from #26984. That issue covers the CLI leaking pipes and orphan processes for MCP stdio servers it starts; this one is the Python SDK's own handles to the app-server process.
- Our workaround would be to close
proc.stdout/proc.stderrourselves afterclose(). That means reaching intoCodexClient._proc/AsyncCodexClient._sync, which are private, so we'd rather see it fixed in the SDK.
- Lenguaje dominante
- Rust
- Estrellas
- 133k
- Forks
- 21.1k
- Merge medio
- 1 min
- PR fusionados (30 d)
- 995
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de openai/codex
-
app-server bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
openai/codex#60595 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
CLI / TypeScript SDK: image paths containing commas are split into separate attachmentsPosiblemente ocupada @akhil9tiet la tomó hoy. Abiertobug CLI exec
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
openai/codex#60587 · 3 comentarios ·
Los mantenedores suelen responder en 1 día
-
bug CLI
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
openai/codex#60494 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
app bug pets
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
Todos los issues de openai/codex
Issues similares
-
SPLADE `/embed_sparse` output depends on the other inputs in the batch (candle BertModel / DistilBertModel)Posiblemente ocupada @0z5a la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
huggingface/text-embeddings-inference#979 ·
Los mantenedores suelen responder en 1 día
-
good first issue help wanted
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
servo/rust-smallvec#874 ·
-
Expression with ill-formed typeAbierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 94/100
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 2 días
-
ci enhancement need-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
Los mantenedores suelen responder en 1 día