File tree Expand file tree Collapse file tree
web/src/app/api/v1/freebuff/session Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -50,6 +50,20 @@ async function callSession(
5050 if ( resp . status === 404 ) {
5151 return { status : 'disabled' }
5252 }
53+ // 403 with a country_blocked body is a terminal signal, not an error — the
54+ // server rejects non-allowlist countries up front (see session _handlers.ts)
55+ // so users don't wait through the queue only to be rejected at chat time.
56+ // The 403 status (rather than 200) is deliberate: older CLIs that don't
57+ // know this status treat it as a generic error and back off on the 10s
58+ // error-retry cadence instead of tight-polling an unrecognized 200 body.
59+ if ( resp . status === 403 ) {
60+ const body = ( await resp . json ( ) . catch ( ( ) => null ) ) as
61+ | FreebuffSessionResponse
62+ | null
63+ if ( body && body . status === 'country_blocked' ) {
64+ return body
65+ }
66+ }
5367 if ( ! resp . ok ) {
5468 const text = await resp . text ( ) . catch ( ( ) => '' )
5569 throw new Error (
Original file line number Diff line number Diff line change @@ -110,7 +110,9 @@ describe('POST /api/v1/freebuff/session', () => {
110110 makeReq ( 'ok' , { cfCountry : 'FR' } ) ,
111111 makeDeps ( sessionDeps , 'u1' ) ,
112112 )
113- expect ( resp . status ) . toBe ( 200 )
113+ // 403 (not 200) so older CLIs that don't know `country_blocked` fall into
114+ // their error-retry backoff instead of tight-polling.
115+ expect ( resp . status ) . toBe ( 403 )
114116 const body = await resp . json ( )
115117 expect ( body . status ) . toBe ( 'country_blocked' )
116118 expect ( body . countryCode ) . toBe ( 'FR' )
@@ -143,7 +145,7 @@ describe('GET /api/v1/freebuff/session', () => {
143145 makeReq ( 'ok' , { cfCountry : 'FR' } ) ,
144146 makeDeps ( sessionDeps , 'u1' ) ,
145147 )
146- expect ( resp . status ) . toBe ( 200 )
148+ expect ( resp . status ) . toBe ( 403 )
147149 const body = await resp . json ( )
148150 expect ( body . status ) . toBe ( 'country_blocked' )
149151 expect ( body . countryCode ) . toBe ( 'FR' )
Original file line number Diff line number Diff line change @@ -20,14 +20,19 @@ import type { NextRequest } from 'next/server'
2020 * the caller's country and it's not on the allowlist, short-circuit with a
2121 * terminal `country_blocked` response so the CLI can show the warning
2222 * screen without ever joining the queue. Null country (VPN / localhost)
23- * fails open — chat/completions will catch it later if it matters. */
23+ * fails open — chat/completions will catch it later if it matters.
24+ *
25+ * Returns HTTP 403 (not 200) so older CLIs — which don't know the
26+ * `country_blocked` status and would tight-poll on an unrecognized 200
27+ * body — fall into their existing `!resp.ok` error path and back off on
28+ * the 10s error retry cadence. The new CLI parses the 403 body directly. */
2429function countryBlockedResponse ( req : NextRequest ) : NextResponse | null {
2530 const countryCode = getCountryCode ( req )
2631 if ( ! countryCode ) return null
2732 if ( FREE_MODE_ALLOWED_COUNTRIES . has ( countryCode ) ) return null
2833 return NextResponse . json (
2934 { status : 'country_blocked' , countryCode } ,
30- { status : 200 } ,
35+ { status : 403 } ,
3136 )
3237}
3338
You can’t perform that action at this time.
0 commit comments