Skip to content

Agents panel: Codex limits always "unavailable" — rpc_request misses replies already buffered by readline() #13773

Closed as duplicate

Description

@Andrelyn12035

System

  • Omarchy 4.0.4-1
  • CPU: 13th Gen Intel Core i7-13700H
  • GPU: Intel Raptor Lake-P (Iris Xe)
  • codex-cli 0.156.1 (logged in with ChatGPT, Plus plan)

What happens

The agents panel shows Codex usage from local stats, but never its plan or limits.
~/.local/state/omarchy/agents/usage/codex.json always ends with:

"limits": [],
"tierLabel": "",
"usageStatusText": "Codex limits unavailable",
"authHelpText": "account/read"

"account/read" is the TimeoutError(method) raised by rpc_request in
bin/omarchy-agent-usage-codex.

Expected

Plan and the 5-hour / weekly limits, which the app-server returns fine:
account/read answers in ~0.5 s and account/rateLimits/read in ~0.8 s.

Steps to reproduce

  1. Codex 0.156.1 logged in with ChatGPT.
  2. omarchy agent usage update --force codex
  3. jq '{usageStatusText, authHelpText, limits}' ~/.local/state/omarchy/agents/usage/codex.json
    → Codex limits unavailable / account/read / [], every run.

Cause

Right after initialized, codex 0.156.1 emits two notifications
(remoteControl/status/changed, account/updated). They arrive in the same
chunk as the reply to account/read.

rpc_request waits with select.select([proc.stdout], ...) and then calls
proc.stdout.readline(). proc.stdout is a buffered text stream: the first
readline() pulls the whole chunk into Python's buffer and returns one line.
The reply is now in that buffer, not in the pipe, so select() never reports
the fd readable again and the loop spins until the 4 s timeout. The exception
also skips account/rateLimits/read, so no limits are recorded.

Traced from inside the collector:

  0.1s <- {"id":1,"result":{...}}                           (initialize)
  0.5s <- {"method":"remoteControl/status/changed",...}
  0.5s <- {"method":"account/updated","params":{"authMode":"chatgpt","planType":"plus"}}
total 4.3s  -> "Codex limits unavailable", "account/read"

With the only change being to skip select() (blocking readline()), the same
code returns in 0.9 s with tierLabel: "plus" and both limit windows.

The code is unchanged on master (select.select([proc.stdout], [], [], 0.25)
in rpc_request), so this affects any codex version that sends notifications
before a reply.

Suggested fix

Don't mix select() with a buffered reader. For example, read stdout on a
background thread into a queue and wait on the queue with the timeout:

import queue, threading

def start_reader(proc):
  lines = queue.Queue()
  def pump():
    for line in proc.stdout:
      lines.put(line)
    lines.put(None)
  threading.Thread(target=pump, daemon=True).start()
  return lines

def rpc_request(proc, lines, request_id, method, params=None, timeout=8):
  payload = {"id": request_id, "method": method, "params": params or {}}
  proc.stdin.write(json.dumps(payload) + "\n")
  proc.stdin.flush()
  deadline = time.time() + timeout
  while (remaining := deadline - time.time()) > 0:
    try:
      line = lines.get(timeout=remaining)
    except queue.Empty:
      break
    if line is None:
      break
    try:
      message = json.loads(line)
    except Exception:
      continue
    if message.get("id") == request_id:
      return message
  raise TimeoutError(method)

(fetch_codex_rpc creates lines = start_reader(proc) once after Popen and
passes it to each call.) Alternatively, open the pipe unbuffered in binary mode
and split lines from os.read() yourself.

Activity

  1. jandrusk commented on Oct 1, 2026

    @jandrusk

    Clear root-cause write-up — thank you.

    This is the same buffered-readline / select race already tracked by the Codex limits cluster. Prefer consolidating on the open fix PRs rather than a third parallel diagnosis:

    Suggest closing this as a duplicate of #13703 / tracking via #13816 once one of those lands. UX follow-up for method names leaking into authHelpText is #13740 / #13059 (separate from the race).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions