From 9c907bdb61c4bb313703879f5243c9d93dec67cb Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 12 Nov 2026 07:37:22 +0000 Subject: [PATCH 1/2] test(desktop): bearer bootstrap waits through a 30 s backend stall Reproduces #23378: the 15 s overall retry budget allows one 10 s /oauth/token attempt plus half of a retry, so a backend busy for 30 s after readiness fails the desktop bearer bootstrap. Fails on main. AI-assisted; verified with the tests and tools listed above. --- .../DesktopLocalEnvironmentAuth.test.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.test.ts b/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.test.ts index e82e3afea7303..c15ce3932bb80 100644 --- a/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.test.ts +++ b/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.test.ts @@ -1,11 +1,13 @@ import * as NodeCrypto from "@effect/platform-node/NodeCrypto"; import { assert, describe, it } from "@effect/vitest"; +import * as Clock from "effect/Clock"; import * as Deferred from "effect/Deferred"; import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as Ref from "effect/Ref"; +import * as Result from "effect/Result"; import * as HttpClient from "effect/http/HttpClient"; import * as HttpClientRequest from "effect/http/HttpClientRequest"; import * as HttpClientResponse from "effect/http/HttpClientResponse"; @@ -238,4 +240,34 @@ describe("DesktopLocalEnvironmentAuth", () => { assert.strictEqual(yield* Ref.get(requestCount), 1); }).pipe(Effect.provide(TestClock.layer())), ); + + it.effect("waits through a backend that stalls for 30 seconds after it is ready", () => + Effect.gen(function* () { + const requestCount = yield* Ref.make(0); + const layerHttpClient = Layer.succeed( + HttpClient.HttpClient, + HttpClient.make((request) => + Effect.gen(function* () { + yield* Ref.update(requestCount, (count) => count + 1); + const now = yield* Clock.currentTimeMillis; + return now < 30_000 ? yield* Effect.never : tokenResponse(request); + }), + ), + ); + const auth = yield* DesktopLocalEnvironmentAuth.DesktopLocalEnvironmentAuth.pipe( + Effect.provide( + DesktopLocalEnvironmentAuth.layer.pipe( + Layer.provide(Layer.mergeAll(layerPool, layerHttpClient, NodeCrypto.layerMac)), + ), + ), + ); + + const fiber = yield* auth.getBearerToken.pipe(Effect.result, Effect.forkChild); + yield* TestClock.adjust(Duration.seconds(45)); + const result = yield* Fiber.join(fiber); + + assert.isTrue(Result.isSuccess(result)); + assert.strictEqual(yield* Ref.get(requestCount), 4); + }).pipe(Effect.provide(TestClock.layer())), + ); }); From a7d9f1b35f4f00ad3d855a8ce3a4fb22267aa97b Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 12 Nov 2026 07:41:10 +0000 Subject: [PATCH 2/2] fix(desktop): a backend busy right after startup no longer ends in a fatal HTTP 500 screen The desktop bearer bootstrap capped its whole transient-retry loop at 15 s, which fits only one 10 s /oauth/token attempt. A backend stalled for longer right after reporting ready failed the bootstrap, and the web client showed a fatal error screen that stayed after the backend recovered. Raise the budget to 60 s so the bootstrap keeps retrying through such stalls. Fixes pingdotgg/t3code#23378 AI-assisted; verified with the tests and tools listed above. --- apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts b/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts index 6c7d24623d40f..89aa3cd2a678c 100644 --- a/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts +++ b/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts @@ -17,7 +17,8 @@ import * as HttpClient from "effect/http/HttpClient"; import * as DesktopBackendPool from "./DesktopBackendPool.ts"; -const BOOTSTRAP_TRANSIENT_RETRY_TIMEOUT = Duration.seconds(15); +// Each /oauth/token attempt may take 10 s, so a backend busy after ready needs several attempts. +const BOOTSTRAP_TRANSIENT_RETRY_TIMEOUT = Duration.seconds(60); const BOOTSTRAP_TRANSIENT_RETRY_INTERVAL = Duration.millis(500); const TRANSIENT_BOOTSTRAP_STATUS_CODES = new Set([502, 503, 504]);