WikifitaGitHub live67e8de5
pesquisa · openai-research/flow-a-403-cases

Flow A 403 Cases — Every Failure and What It Taught

The complete catalog of 403s encountered while closing flow A — the exact causes, per-case evidence, and the lessons that led to the working sequence

Baixar raw

Flow A 403 Cases — Every Failure and What It Taught

Part of openai-research. Each case below was observed live with captured evidence; each is a distinct cause (or a validated non-cause). The final column is what the failure established.

The 403 catalog

#What was attemptedResponseRoot cause (proved by)
1Challenge with a fresh generated did (02987bd6 / 04bd1580)403The did is installation identity (persisted, never regenerated). Proof: same token, did a05f9cdd → 200. The server binds oai-did↔device; an unknown did dies at the challenge.
2/f/conversation with the PKCE-session access (any run of the A0 line)403 "Unusual activity"The session created by a manual browser authorize is not recognized by the edge for f/*; only the app-server session (auth.json/live authToken) passes. Proof: A/B (the same request with app-session access → 200).
3/f/conversation with x-conduit-token403The app's own winning requests carry no conduit header (the conduit is prepare-only; the cluster pin it carries was the suspected differentiator — and its absence is what the app does). Proof: app request 200 without it; our request without it → 200.
4Challenge computed over node:https while the conversation goes via the main (A-1 step)403Same-path invariant: challenge+prepare+conv must come from the same transport/pool (the app always uses the main). Fix: everything through the conv trigger.
5local_function_signatures without the handoff (only the custom tool) in a normal turn403The server expects the handoff signature in normal conversation turns (the app always sends it; 100% of captured 200s include it).
6Continuation (2nd text turn) without parent_message_id403The app always supplies conversation_id + parent_message_id on follow-up turns (verified in captured payloads).
7Re-executing a request (curl re-run of the same /run/conv input)403The app_attest_challenge is one-time; reuse of the same input/request → 403 (and the earlier verified 200 was overwritten by my own re-run).
8/devicecheck with a revoked PKCE access (after the user killed the sessions)401 token_revokedThe access token had been revoked (the earlier PKCE sessions were logged out); not a protocol issue.
9(Non-403, but a lesson)static auth.json after an app-server refreshThe live getAuthStatus {includeToken:true} returns the current session token; the disc file can lag (a refresh at 16:11 changed the file and all requests with the stale access became 403 until the live token was used).

The two broken chains that caused most of the damage

  1. "Own session" chain (A0): the A0 probe created its own PKCE session, used a regenerated did, and attached the conduit — three deviations from the app in one request → 403 stack (cases 1–3). The system closed it: the session must be the app-server's, the did must be the installation's, and the conduit must not travel.
  2. Mixed-transport chain (A-1): the probe fetched the challenge over its own HTTP while the conversation went through the main (case 4) → the challenge↔conversation pairing is source-bound.

What the failures established (the final set of invariants)

  • oai-did: persisted installation identity (live renderer storage; never regenerate).
  • Session access: the live app-server authToken (getAuthStatus includeToken) — not a fresh login, not the stale file.
  • Transport: challenge+prepare+conv through the same main/electron path.
  • No x-conduit-token on /f/conversation.
  • local_function_signatures with the handoff present in normal turns.
  • conversation_id + parent_message_id on every turn after the first.
  • app_attest_challenge is one-time per request (never reuse/re-run an input).

Cross-references