diff --git a/apps/server/src/provider/GrokProvider.test.ts b/apps/server/src/provider/GrokProvider.test.ts index 9cc1a9a7e3238..9be799112cc23 100644 --- a/apps/server/src/provider/GrokProvider.test.ts +++ b/apps/server/src/provider/GrokProvider.test.ts @@ -722,6 +722,36 @@ it.layer(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer))("readGrokAccount" } }), ); + + it.effect("tells accounts in Legacy (no storage) to switch to Privacy Mode", () => + Effect.gen(function* () { + const { usageLimits: limits } = yield* readGrokAccount({ + HOME: "/definitely/not/a/grok-home", + GROK_AUTH: '{"https://accounts.x.ai/sign-in":{"key":"private-token"}}', + }).pipe( + Effect.provideService( + HttpClient.HttpClient, + HttpClient.make((request) => + Effect.succeed( + HttpClientResponse.fromWeb( + request, + Response.json( + { + error: + "Grok Build is not supported in Legacy (no storage). Switch to Privacy Mode to use Grok Build. [WKE=privacy-mode:legacy]", + }, + { status: 403 }, + ), + ), + ), + ), + ), + ); + expect(limits.unavailable?.reason).toBe("probeFailed"); + expect(limits.unavailable?.message).toMatch(/Privacy Mode/); + expect(limits.unavailable?.message).not.toMatch(/WKE=|Grok Build is not supported/); + }), + ); }); it.layer(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer))("readGrokAccount email", (it) => { diff --git a/packages/provider-grok/src/server/usageLimits.ts b/packages/provider-grok/src/server/usageLimits.ts index 8036df0dd3e99..c6ba583c139ce 100644 --- a/packages/provider-grok/src/server/usageLimits.ts +++ b/packages/provider-grok/src/server/usageLimits.ts @@ -38,6 +38,7 @@ const GrokUsageResponse = Schema.Struct({ }), ), }); +const GrokErrorResponse = Schema.Struct({ error: Schema.String }); function parsePeriodBoundary(value: string | undefined) { if ( @@ -182,6 +183,17 @@ export const readGrokAccount = Effect.fn("readGrokAccount")(function* ( HttpClientRequest.bearerToken(token), ), ); + if (response.status === 403) { + const { error } = yield* HttpClientResponse.schemaBodyJson(GrokErrorResponse)(response); + // xAI refuses Grok Build while the account's privacy setting is a legacy mode. + if (error.includes("WKE=privacy-mode:")) { + return makeUnavailableUsageLimits({ + checkedAt, + reason: "probeFailed", + message: "Switch your xAI account to Privacy Mode to show Grok usage limits.", + }); + } + } const body = yield* HttpClientResponse.schemaBodyJson(GrokUsageResponse)( yield* HttpClientResponse.filterStatusOk(response), );