ZhuLinsen/daily_stock_analysis · error · CodexAppServerError

resource_cleanup_failed

resource_cleanup_failed

Error message

Codex compatibility-check process group could not be reclaimed

What it means

Raised by _terminate_probe_process_group (src/services/agent_backend_status_service.py:78) with code 'resource_cleanup_failed' after the Codex compatibility-check probe process group survived SIGTERM, a grace wait, SIGKILL, and a further 2.0s kill grace (_PROBE_KILL_GRACE_SECONDS). The service spawns the Codex CLI in its own process group and must reclaim both the launcher and its native child; if os.killpg(pid, 0) still signals a live group afterwards, this RuntimeError (CodexAppServerError) fires to surface leaked processes instead of silently continuing.

Source

Thrown at src/services/agent_backend_status_service.py:78

    if process.poll() is None or _probe_process_group_alive(process_group_id):
        try:
            os.killpg(process_group_id, signal.SIGKILL)
        except ProcessLookupError:
            pass
        except PermissionError:
            if process.poll() is None:
                process.kill()
        kill_deadline = time.monotonic() + _PROBE_KILL_GRACE_SECONDS
        if process.poll() is None:
            try:
                process.wait(timeout=max(0.0, kill_deadline - time.monotonic()))
            except subprocess.TimeoutExpired:
                pass
        while _probe_process_group_alive(process_group_id) and time.monotonic() < kill_deadline:
            time.sleep(0.02)

    if process.poll() is None or _probe_process_group_alive(process_group_id):
        raise CodexAppServerError(
            "resource_cleanup_failed",
            "Codex compatibility-check process group could not be reclaimed",
        )


def _run_codex_probe(
    command: list[str],
    *,
    timeout: float,
    capture_output: bool = False,
) -> subprocess.CompletedProcess:
    """Run one bounded CLI probe without orphaning the native Codex child."""
    process = subprocess.Popen(
        command,
        stdout=subprocess.PIPE if capture_output else subprocess.DEVNULL,
        stderr=subprocess.PIPE if capture_output else subprocess.DEVNULL,
        text=capture_output,
        env=controlled_environment(),

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Inspect leftover processes: `ps -eo pid,pgid,stat,cmd | grep -i codex` and look for D-state or traced processes; kill them manually (`kill -9 -<pgid>`).
  2. If a debugger/tracer is attached, detach it so SIGKILL can be delivered.
  3. In restricted containers, ensure the process has CAP_KILL over its own children or run without a pid namespace restriction on signals.
  4. Upgrade/replace a Codex CLI version known to hang, and check the probe timeout that precedes cleanup.
  5. Catch CodexAppServerError with code 'resource_cleanup_failed' and report backend status as degraded rather than crashing the status service.

Example fix

# before
status = run_codex_compatibility_check()  # may raise, kills the API call

# after
try:
    status = run_codex_compatibility_check()
except CodexAppServerError as exc:
    if exc.args[0] == 'resource_cleanup_failed' or 'resource_cleanup_failed' in str(exc):
        status = {'available': False, 'reason': 'codex probe could not be reclaimed'}
    else:
        raise
Defensive patterns

Strategy: try-catch

Try / catch

from src.agent.codex_app_server_transport import CodexAppServerError
try:
    result = run_codex_compatibility_check()
except CodexAppServerError as exc:
    if 'resource_cleanup_failed' in str(exc):
        # surface degraded status; optionally log leaked pgid for manual reaping
        result = {'available': False, 'reason': 'probe_process_leak'}
    else:
        raise

Prevention

When it happens

Trigger: Running a Codex backend compatibility check where the CLI's native child is stuck in an uninterruptible state (D-state on I/O), is being traced/debugged (SIGKILL deferred), or was re-parented into a new process group so killpg misses it; also possible on permission-restricted environments where SIGKILL to the group fails with PermissionError.

Common situations: Containers/ci sandboxes with pid limits or seccomp filters blocking signals to process groups; a hung Codex binary (filesystem stall, GPU driver hang); running under strace/gdb which traps signals; zombie children kept alive by a supervisor.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/adfd707b2fda6a6f. Report an issue: GitHub.