From d1bd7b00df6c9e6aa99ef6f16e8081f2722e0dcc Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Fri, 11 Sep 2026 02:56:15 -0700 Subject: [PATCH] fix(desktop): dispatch probe falls back to /api/status on pre-/api/health remotes Switching the pooled dispatch probe to /api/health (salvaged from #97914) would 404 on every dispatch against a remote older than 0.19, retire the tunnel and reconnect forever - the same storm #107997 describes, moved to old backends. Fall back to /api/status on an explicit 404 exactly the way the boot readiness probe already does (backend-health.ts). The legacy fallback idea and its test are taken from #101976 (@edosulai); the rest of that PR (timeout-tolerance streak, ServerAlive SSH options) is not adopted. Co-authored-by: Edo Sulaiman --- apps/desktop/electron/remote-liveness.test.ts | 30 +++++++++++++++++++ apps/desktop/electron/remote-liveness.ts | 21 +++++++++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/apps/desktop/electron/remote-liveness.test.ts b/apps/desktop/electron/remote-liveness.test.ts index 70e8d45396..fe268bfe60 100644 --- a/apps/desktop/electron/remote-liveness.test.ts +++ b/apps/desktop/electron/remote-liveness.test.ts @@ -329,6 +329,36 @@ describe('ensureHealthyPooledRemoteBackendForDispatch', () => { expect(retire).toHaveBeenCalledOnce() expect(reconnect).toHaveBeenCalledOnce() }) + + it('falls back to /api/status when /api/health returns 404 on older backends', async () => { + const legacy = { baseUrl: 'http://127.0.0.1:49525', mode: 'remote' } + const legacyPromise = Promise.resolve(legacy) + + const retire = vi.fn() + const reconnect = vi.fn() + + const probe = vi.fn(async (_connection, path) => { + if (path === '/api/health') { + throw new Error('404: Not Found') + } + }) + + await expect( + ensureHealthyPooledRemoteBackendForDispatch({ + connectionPromise: legacyPromise, + currentConnectionPromise: () => legacyPromise, + probe, + reconnect, + retire + }) + ).resolves.toBe(legacy) + + expect(probe).toHaveBeenCalledWith(legacy, '/api/status', { + timeoutMs: POOLED_REMOTE_DISPATCH_PROBE_TIMEOUT_MS + }) + expect(retire).not.toHaveBeenCalled() + expect(reconnect).not.toHaveBeenCalled() + }) }) describe('revalidatePooledRemoteBackends', () => { diff --git a/apps/desktop/electron/remote-liveness.ts b/apps/desktop/electron/remote-liveness.ts index 2160abce6b..f9ab632c6a 100644 --- a/apps/desktop/electron/remote-liveness.ts +++ b/apps/desktop/electron/remote-liveness.ts @@ -1,3 +1,5 @@ +import { isMissingHealthEndpointError } from './backend-health' + export const REMOTE_LIVENESS_TIMEOUT_MS = 10_000 // Dispatch is synchronous user intent: a cached descriptor must prove its // forwarded endpoint is alive before it can be returned. Probe cheap @@ -102,9 +104,22 @@ export async function ensureHealthyPooledRemoteBackendForDispatch