ZhuLinsen/daily_stock_analysis · error · CodexAppServerError

resource_cleanup_failed

resource_cleanup_failed

Error message

Codex App Server process group could not be reclaimed

What it means

Raised by the transport's stop/cleanup path when, after SIGTERM, then SIGKILL of the child and a 2-second bounded wait (kill_deadline), the process still has poll() is None or its process group remains alive (_process_group_alive checks the pgid). The App Server is spawned in its own process group precisely so it can be reclaimed wholesale.

Source

Thrown at src/agent/codex_app_server_transport.py:1048

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

            if process.poll() is None or _process_group_alive(process_group_id):
                raise CodexAppServerError(
                    "resource_cleanup_failed",
                    "Codex App Server process group could not be reclaimed",
                )


def resolve_command(executable: str = "codex") -> list[str]:
    """Resolve the fixed App Server argv and least-privilege overrides."""
    if is_native_windows():
        raise CodexAppServerError(
            "capability_unsupported",
            "Codex App Server Agent is not supported on native Windows in this phase",
        )
    resolved = shutil.which(executable)
    if resolved is None:
        raise CodexAppServerError("command_not_found", "Codex executable was not found")
    command = [resolved, "app-server", "--stdio"]
    for override in _BASE_CONFIG_OVERRIDES:
        command.extend(["-c", override])

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Inspect which PIDs remain: ps -o pid,ppid,pgid,stat -g <pgid> to find survivors (look for 'D' state or stray grandchildren)
  2. If an MCP child escaped the group, kill it explicitly by PID or file the leak as a codex bug
  3. Increase the kill_deadline (currently hardcoded 2s) if the machine is slow, or retry the group kill once
  4. Call stop() from the same thread that owns the Popen, or ensure waitpid is not blocked by threading constraints
Defensive patterns

Strategy: retry

Try / catch

try:
    transport.stop()
except CodexAppServerError as exc:
    if exc.code == "resource_cleanup_failed":
        os.killpg(pgid, signal.SIGKILL)  # second, manual sweep
        time.sleep(1)
        # log survivors; leak is bounded and known
        logger.warning("app-server pgid %d needed manual sweep", pgid)

Prevention

When it happens

Trigger: SIGKILL was delivered but the child is stuck in uninterruptible sleep (D state) on NFS/device IO, or the process group still contains live grandchildren (MCP servers spawned by codex) that ignore/escape the group kill; also possible zombie state never reaped because wait() was called from a different thread than the Popen owner.

Common situations: Running under a container/node with IO stalls; codex spawning MCP servers outside the killed pgid (setsid'd helpers); zombie accumulation when Popen.wait() races; heavy load making the 2s deadline too tight.

Related errors


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