AI 개발/에이전트 코딩

Codex 주간 한도가 갑자기 사라질 때: 서버가 보내는 계량값과 내 로그 대조법

뉴랜드 2026. 10. 21. 08:41
반응형

Codex 주간 한도가 10월 16일 밤에 갑자기 0%가 된 신고가 잇따랐고, OpenAI는 적용 한도가 선택한 플랜과 어긋난 계정이 약 40,000개라고 밝혔습니다. 한도 퍼센트는 내 컴퓨터에서 계산되지 않고 서버 응답 헤더로만 도착합니다. 내 세션 로그로 계량 오류와 실제 사용을 가르는 순서를 정리합니다.

2026년 10월 16일 18시(UTC)에 벌어진 일

2026-10-16 18:08~18:12 UTC에 여러 계정의 주간 잔여량이 수십 초 만에 0%로 떨어졌습니다. GitHub 이슈 openai/codex #54677은 2026-10-16 19:02 UTC에 열렸고 2026-10-21 기준 아직 열려 있으며 댓글이 45개 붙었습니다. 최초 신고자는 21:10:43 MSK(18:10:43 UTC)에 주간 잔여 77%, 38초 뒤인 21:11:21 MSK(18:11:21 UTC)에 10%를 기록했습니다. 다른 신고자들도 46%→100% 사용, 62%→0% 잔여, 49%→0% 같은 값을 같은 시간대에 남겼습니다.

OpenAI의 Tibo는 2026-10-20에 X에서 약 40,000개 계정의 적용 한도가 선택한 플랜과 어긋나 있었다고 밝혔습니다. 원문은 "We've identified approximately 40,000 accounts that had an inconsistency on which usage limits where the actual usage that was applied did not correspond to their actual selected plan"입니다.

원인 설명이 더 구체적인 사례도 있습니다. 이슈에 공개된 OpenAI 메일("Correcting your usage limit")은 2026-10-06에 Pro 200달러에서 100달러 플랜으로 내린 뒤 이전 플랜의 주간 한도가 그대로 남아 있었고 그 오류를 바로잡는 시점에 이미 쌓인 사용량이 낮은 한도에 반영됐다고 적었습니다. 보상은 12,500 크레딧(500달러 상당)이었고 만료일은 2027-01-20이었습니다. 주간 한도 자체가 되돌아온 것은 아닙니다.

여기서 확정할 수 있는 범위와 아닌 범위를 나눠야 합니다. 다운그레이드 계정의 10월 16일 급감은 OpenAI가 서면으로 인정했습니다. 반면 플랜을 바꾸지 않은 Plus 사용자의 신고는 2026-10-20까지도 답을 받지 못했습니다. 그래서 "10월 16일 이후 한도가 이상하다"는 관찰만으로 원인을 단정하면 안 됩니다.

한도 퍼센트는 서버 응답 헤더에서 온다

Codex 소스(rust-v0.163.0)를 보면 한도 표시 경로가 분명합니다. codex-rs/codex-api/src/rate_limits.rs의 parse_rate_limit_for_limit 함수가 응답 헤더에서 값을 꺼내 RateLimitSnapshot을 만듭니다.

x-codex-primary-used-percent
x-codex-primary-window-minutes
x-codex-primary-reset-at
x-codex-secondary-used-percent
x-codex-secondary-window-minutes
x-codex-secondary-reset-at
x-codex-rate-limit-reached-type
x-codex-credits-has-credits
x-codex-credits-balance

codex-rs/protocol/src/protocol.rs의 구조체는 창 하나를 used_percent, window_minutes, resets_at 세 값으로 정의합니다. 주석에 used_percent는 "Percentage (0-100) of the window that has been consumed", window_minutes는 "Rolling window duration, in minutes", resets_at은 "Unix timestamp (seconds since epoch) when the window resets"라고 적혀 있습니다.

이 구조에서 중요한 결론이 하나 나옵니다. 클라이언트는 토큰을 세어 퍼센트를 만들지 않습니다. 서버가 준 숫자를 표시하고 로그에 남길 뿐입니다. 그래서 로컬 토큰 합계와 화면 퍼센트가 맞지 않는 것을 계산 실수로 보면 안 됩니다. 구조상 나올 수밖에 없는 차이입니다. "내가 쓴 토큰은 120만 개인데 왜 100%인가"라는 질문은 로컬에서 답이 나오지 않습니다.

응답 구조를 정의한 codex-rs/app-server-protocol/src/protocol/v2/account.rs에는 ordinary_usage_allowed 필드도 있습니다. 주석은 "clients must not infer recovery from percentages or reset times"라고 못 박습니다. 복구 여부도 서버가 정한다는 뜻입니다.

내 로그에서 주간 창 이력만 꺼내기

세션 기록은 ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl에 날짜별로 쌓입니다. 각 줄에서 event_msg 안의 token_count 이벤트를 보면 rate_limits가 함께 저장됩니다. 실제 저장 형식은 다음처럼 생겼습니다.

{"timestamp":"2026-10-16T18:07:03.000Z","type":"event_msg","payload":{"type":"token_count","info":{"total_token_usage":{"total_tokens":1200}},"rate_limits":{"primary":{"used_percent":22.0,"window_minutes":300,"resets_at":1792650000},"secondary":{"used_percent":28.0,"window_minutes":10080,"resets_at":1792649598},"plan_type":"prolite"}}}

주간 창은 window_minutes가 10080(7일)인 창입니다. 5시간 창은 300입니다. 플랜에 따라 창 구성이 다를 수 있으므로 primary와 secondary의 위치를 외우지 말고 window_minutes로 골라야 합니다.

파이썬 3으로 주간 창 이력과 급변 지점을 뽑는 스크립트입니다.

import json, pathlib
root = pathlib.Path.home() / ".codex" / "sessions"
rows = []
for f in sorted(root.rglob("rollout-*.jsonl")):
    for line in f.open(encoding="utf-8", errors="ignore"):
        try:
            d = json.loads(line)
        except Exception:
            continue
        p = d.get("payload") or {}
        if d.get("type") != "event_msg" or p.get("type") != "token_count":
            continue
        rl = p.get("rate_limits") or {}
        win = next((w for w in (rl.get("secondary"), rl.get("primary"))
                    if w and w.get("window_minutes") == 10080), None)
        if not win:
            continue
        info = (p.get("info") or {}).get("total_token_usage") or {}
        rows.append((d.get("timestamp"), win.get("used_percent"),
                     win.get("resets_at"), info.get("total_tokens", 0), rl.get("plan_type")))
rows.sort()
for ts, used, reset, tokens, plan in rows:
    print(f"{ts}  used={used:>5}  reset={reset}  tokens={tokens:>8}  plan={plan}")
print("--- 같은 reset 구간에서 used_percent가 20%p 이상 뛴 지점")
for a, b in zip(rows, rows[1:]):
    if a[2] == b[2] and a[1] is not None and b[1] is not None and b[1] - a[1] >= 20:
        print(f"{a[0]} -> {b[0]}: {b[1] - a[1]:+.1f}%p, 토큰 {b[3] - a[3]:+d}")

샘플 로그로 돌려 확인한 출력은 다음과 같습니다. 18:07에 28%, 18:31에 100%로 뛰었는데 그 사이 세션 토큰 증가는 700개뿐이었습니다.

2026-10-16T18:07:03.000Z  used= 28.0  reset=1792649598  tokens=    1200  plan=prolite
2026-10-16T18:31:00.000Z  used=100.0  reset=1792649598  tokens=    1900  plan=prolite
--- 같은 reset 구간에서 used_percent가 20%p 이상 뛴 지점
2026-10-16T18:07:03.000Z -> 2026-10-16T18:31:00.000Z: +72.0%p, 토큰 +700

세션 파일마다 토큰 합계의 시작점이 다르므로 파일을 넘나드는 토큰 차이는 비교하지 않았습니다. 한 세션 안에서 같은 resets_at을 가진 두 스냅숏만 비교하는 것이 안전합니다.

계량 오류인지 실제 사용인지 가르는 신호

아래 다섯 가지를 순서대로 보면 대부분 판정이 됩니다. 하나라도 걸리면 지원 문의에 그대로 붙일 근거가 됩니다.

  • resets_at이 그대로인데 used_percent가 20%p 이상 상승했다면 계량 쪽 가능성이 큽니다. 위 스크립트의 급변 출력에서 이 조합을 먼저 봅니다.
  • 같은 구간 세션 토큰 증가가 수백~수천 개에 그쳤다면 정상 사용으로 70%p를 만들 수 없는 규모입니다. 스크립트의 tokens 열이 이 값을 보여 줍니다.
  • 5시간 창은 정상인데 주간 창만 급락했다면 주간 버킷만 어긋난 경우입니다. 두 창의 used_percent를 나란히 놓고 비교합니다.
  • 최근 30일 안에 플랜을 내렸다면 2026-10-06~16 사례와 같은 유형입니다. 결제 화면과 로그의 plan_type을 함께 봅니다.
  • ChatGPT Work, iOS, 웹에서 같은 시간대에 작업했다면 한도를 공유하므로 다른 기기 사용이 원인일 수 있습니다.

반대로 resets_at이 바뀌었거나 로컬 토큰이 급변 구간에서 수십만~수백만 개 늘었다면 계량 오류로 보기 어렵습니다. 서브에이전트를 여러 개 띄우거나 클라우드 작업을 돌린 세션은 실제 소모가 큰 것이 정상입니다.

지금 확인하고 문의에 넣을 것

먼저 Codex에서 /usage를 실행합니다. 이 명령은 "view account usage or use a usage limit reset"으로 정의되어 있고 실행할 때 서버에 계정 한도를 새로 요청합니다. /status는 세션 설정과 토큰 사용량을 보여 주며 한도 스냅숏이 15분보다 오래되면 값과 함께 "limits may be stale - start new turn to refresh." 경고를 붙입니다. 그래서 화면에 숫자가 없다고 사용량이 사라진 것은 아닙니다.

리셋권은 만료일이 있습니다. 계정 응답 구조에는 available_count와 함께 리셋권별 granted_at, expires_at, status(available/redeeming/redeemed)가 옵니다. 2026-10-20 공개된 보상 사례처럼 크레딧 만료일이 따로 붙는 경우도 있으니 만료가 임박한 리셋권부터 확인해야 합니다. 한도가 이상하다고 리셋권을 먼저 쓰면 원인 확인에 쓸 증거를 잃습니다.

지원 문의에는 다음 다섯 줄을 그대로 넣는 편이 빠릅니다.

  • 급변 전후 타임스탬프와 used_percent 두 값(UTC)
  • 같은 구간 세션 총 토큰 증가량
  • 급변 전후 resets_at 값이 같은지 여부
  • plan_type 값과 플랜 변경일
  • 사용한 표면(CLI, 데스크톱, ChatGPT Work, iOS)

이슈 #54677에 붙은 신고들이 대부분 이 형식을 따랐고 그중 한 명은 서면 설명과 크레딧을 받았습니다.

재발을 감시하는 최소 장치

같은 일이 다시 벌어졌는지 확인하려면 스크립트를 하루 한 번 돌려 결과를 파일로 남기면 됩니다. used_percent가 줄지 않았는데 급변 줄이 새로 생기면 그때가 확인 시점입니다.

0 9 * * * /usr/bin/python3 ~/quota.py >> ~/quota-history.txt 2>&1

한도는 서버가 정하는 값이고 로컬 계산으로 재현되지 않습니다. 그래서 한도가 이상하다고 느낄 때 확인할 것은 내 토큰 합계가 아니라 같은 창 안에서 값이 어떻게 바뀌었는지입니다. 이 순서를 지키면 계량 오류와 실제 사용을 헷갈리지 않고 문의에 필요한 근거도 그 자리에서 만들어집니다.

참고한 1차 출처

반응형