Skip to content

Request logs can miss successful ChatGPT native passthrough turns #44

Closed

Description

@0disoft

Summary

I am seeing a request-log gap for successful ChatGPT/native passthrough turns. Routed providers such as umans and opencode-go appear in /api/logs, but successful gpt-5.5 / chatgpt turns do not show up. A non-stream chatgpt 503 does show up, so the issue seems specific to successful native passthrough SSE finalization rather than all ChatGPT requests.

This looks like a follow-up edge case around the native passthrough SSE workaround from #31.

Observed evidence

Current runtime:

  • ocx status --json reports proxy v2.6.0, PID 32544, health OK, default provider chatgpt.
  • Codex is configured to route through opencodex at http://localhost:10100/v1.
  • /api/logs currently groups as:
    • umans: 104 entries, all 200
    • opencode-go: 18 entries, all 200
    • chatgpt: 1 entry, 503
  • The only chatgpt entry is:
    • local time 2026-06-28 16:44:07 +09:00
    • model gpt-5.5
    • status 503
    • errorCode server_is_overloaded
    • closeReason non_stream
    • usageStatus unreported
  • During the same session, Codex session JSONL showed completed gpt-5.5 turns with model_provider: "opencodex" / comp_hash: "opencodex", but those successful turns were not visible in /api/logs.
  • The proxy stdout log repeatedly contains:
1 | (function (controller, error) {"use strict";
              ^
TypeError: null is not an object

Code path that looks suspicious

In src/server.ts, native passthrough SSE uses upstreamResponse.body.tee():

  • branch 0: returned directly as the client-facing Response body via markNativePassthroughSseResponse(...)
  • branch 1: consumed in the background by consumeForInspection(...)

Then responseWithDeferredRequestLog(...) skips normal request-log wrapping for native passthrough SSE responses:

if (isNativePassthroughSseResponse(response)) {
  return response;
}

So successful native passthrough logs depend on the background inspection path calling onNativePassthroughTerminal.

The cancel path looks like it can suppress finalization:

  • consumeForInspection(...) sets cancelled = true and calls reader.cancel(...) when the signal aborts.
  • The fallback terminal reporting uses if (!reported && !cancelled) onTerminal("incomplete").
  • /v1/responses passes onNativePassthroughCancel, but if the inspection pump exits through the early aborted/cancelled path, it looks possible for neither onNativePassthroughTerminal nor onNativePassthroughCancel to finalize the request log.

That would match the observed pattern:

  • routed providers use the normal logging path and appear in /api/logs
  • non-stream ChatGPT failures appear
  • successful ChatGPT passthrough SSE responses can reach Codex but not finalize a request-log entry

Possible related factor

/api/logs is also memory-only (requestLog: RequestLogEntry[] = [], max 200). If the proxy crashes/restarts during the same window, successful entries from the previous process are lost. That may be a separate limitation, but the native passthrough finalization path still looks worth hardening so a successful/cancelled passthrough request always records exactly one terminal log entry when possible.

Questions

  1. Should native passthrough SSE finalize /api/logs on client cancel / inspection pump abort, probably as 499 with closeReason: "client_cancel"?
  2. Should consumeForInspection(...) call a terminal/cancel callback even when the abort signal sets cancelled = true?
  3. Would you prefer a focused PR with a regression test for native passthrough SSE cancel/finalization, or is this better handled as part of a broader request-log persistence/logging change?

Activity

  1. lidge-jun commented on Jun 29, 2026

    @lidge-jun
    Owner

    PR #46 (@0disoft, fix(logs): finalize native passthrough SSE usage) addresses this. It makes the background inspection finalize /api/logs + usage for native passthrough SSE even when no Codex pool terminal recorder is active — closing the consumeForResponseLogMetadata else-branch gap.

    One follow-up flagged on the PR: the primary symptom here is successful turns going missing because Codex disconnects the client right after consuming, which aborts the shared signal while the teed inspection branch is still draining (consumeForInspection then takes the cancelled path and skips onTerminal). A regression test for success-then-immediate-disconnect, and a decision on a 499/client_cancel cancel finalizer, would fully close #44.

  2. lidge-jun commented on Jun 29, 2026

    @lidge-jun
    Owner

    Merged. PR #46's fix was cherry-picked into the feat/kiro-on-dev working branch as 2481c80 (author attribution to @0disoft preserved).

    Verification on the branch:

    • bun test tests/server-auth.test.ts tests/request-log.test.ts tests/passthrough-abort.test.ts → 57 pass / 0 fail (incl. the new native-passthrough usage regression test)
    • bun x tsc --noEmit → clean

    This closes the secondary gap (native passthrough SSE now finalizes /api/logs + usage even without a Codex pool terminal recorder). The success-then-immediate-disconnect cancel-path case noted on PR #46 remains a good follow-up to fully close this issue. The GitHub PR stays open against dev and will close when it lands there.

  3. lidge-jun commented on Jun 29, 2026

    @lidge-jun
    Owner

    Resolved by the native passthrough SSE finalization fix (2481c80, PR #46), now in dev and released through preview (v2.6.4-preview). Successful ChatGPT native passthrough turns are finalized into /api/logs and /api/usage with a regression test covering the path. Closing as fixed.

  4. added 3 commits that reference this issue on Jul 3, 2026
  5. lidge-jun commented on Jul 3, 2026

    @lidge-jun
    Owner

    For the record — the cancel-path finalization is now fixed (merged to main/preview, ships next release):

    • Native passthrough always uses the finalizable inspection path (2481c80) so successful turns stop going missing.
    • consumeForInspection gained an onCancel finalizer fired on both abort branches, onDone runs on early-abort, and reportNativeTerminal no longer downgrades a detected terminal to a cancel — so a turn logs completed/failed when it finished, 499 client_cancel only on a pure cancel. Idempotent → exactly one /api/logs entry per turn (33aafa0). Regression test: tests/consume-for-inspection-cancel.test.ts.
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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions