Skip to content

fix(api): a removed tenant's refusal carries no CORS headers, so a browser stream re-dials forever #637

Description

@EricAndrechek

Area: api · streaming — footgun · found via #611's follow-ups (PR body)

Expected: a browser client whose tenant has been removed reads the 404 and stops, which is what the SDK is built to do — it stops a stream on a 404 and retries a 503 (clients/ts/src/stream/sse.ts).

Actual: over a nested settings directory with no 0 folder, the refusal carries no CORS headers, so the browser never hands the status to the SDK. corsOrigins (internal/api/router.go:443-462) answers a tenant route from the request's tenant only when the registry still holds it; a tenant it does not hold falls back to tenant.Default, and with no 0 folder served that lookup fails too and the function returns nil. corsMiddleware then writes no Access-Control-Allow-Origin, the fetch rejects, and the transport reports a network error. The SDK treats that as a drop and re-dials — forever, since the tenant is gone for good.

Impact: every browser page open on a removed tenant becomes a permanent re-dial loop against a server that will never serve it again, and the operator sees traffic with no way to tell it from a flapping network. The same fallback makes the 400/404/503 of tenant resolution unreadable to a browser for every unknown tenant, not only a removed one.

#611 names two candidate shapes and picks neither, because the choice is Eric's: (a) whatever fronts WaveHouse turns away an unknown subdomain and answers with CORS headers, the preflight included — which makes this a deployment requirement to document rather than engine work; or (b) the evicted stream sends a final event before it closes, which that connection's own CORS does let the browser read. Distinct from #471, which is a proxy-terminated preflight rejecting Last-Event-ID; this one is WaveHouse's own refusal, and the fix is on the server side.

Related: #611, #600 (refusals read tenant 0's list), #583 (story 3), #471, #469


From #611's "Follow-ups" section (taitelee), flagged there as a design question for Eric and no story owns it; validated by code-read against 93d80198 on 2026-09-25. Filed by the pm-triage routine.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/apiHTTP handlers, routing, middlewarearea/streamingSSE / live-query delivery path (/v1/stream)area/tenantTenant id, header resolution, per-tenant settings (internal/tenant)bugSomething isn't working

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions