fix(trailbase-db-collection): handle 403 on subscribe/* with polling fallback#1521
Open
royalcala wants to merge 1 commit intoTanStack:mainfrom
Open
fix(trailbase-db-collection): handle 403 on subscribe/* with polling fallback#1521royalcala wants to merge 1 commit intoTanStack:mainfrom
royalcala wants to merge 1 commit intoTanStack:mainfrom
Conversation
…fallback
When a TrailBase table has row-level access rules (_ROW_.*), the server
cannot evaluate them for wildcard subscriptions and returns 403 Forbidden.
Previously start() called subscribe("*") without error handling, so the
thrown error prevented markReady() from being called — leaving the
collection in a permanent loading state where useLiveQuery.data is
always undefined.
Additionally, onInsert/onUpdate/onDelete called awaitIds() unconditionally,
which waits for SSE events to confirm persistence. In polling-only mode
seenIds is never populated, so awaitIds would time out after 120 s and
reject, rolling back every optimistic mutation.
Fix:
- Wrap subscribe("*") in try/catch; log a debug message and fall back to
polling when it fails (e.g. 403).
- Hoist sseAvailable flag to closure scope; set it to true only when SSE
connects successfully.
- markReady() is now always called via finally, so the collection becomes
usable even without live updates.
collection reflects server state after the next polling cycle instead.
- periodicCleanupTask is only started when SSE is actually available.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When a TrailBase table has row-level access rules (
_ROW_.*), the server cannot evaluate those predicates for wildcard subscriptions and returns403 ForbiddenonGET /api/records/v1/<table>/subscribe/*.This caused two separate bugs:
Bug 1 — Collection stuck in loading state forever
start()calledsubscribe("*")without any error handling. The thrown403error propagated past thefinallyblock, somarkReady()was never called →useLiveQuery.dataremainedundefinedindefinitely, even thoughlistrequests returned data normally.Bug 2 — Every optimistic mutation rolled back after 120 s
onInsert,onUpdate, andonDeletecalledawaitIds()unconditionally.awaitIdswaits for the subscription SSE stream to confirm the server has persisted the record by checkingseenIds. In polling-only modeseenIdsis never populated (no SSE events arrive), so the wait always timed out →TimeoutWaitingForIdsError→ the optimistic mutation was rolled back.Fix
subscribe("*")intry/catch— log aconsole.debugmessage and continue without SSE when the subscribe call fails (e.g. 403).sseAvailableflag to closure scope — set totrueonly when SSE connects successfully. This allows the three mutation handlers to check it.markReady()is always called viafinally— the collection becomes usable even without live updates.onInsert/onUpdate/onDeleteskipawaitIds()when!sseAvailable— the collection reflects server state after the next polling cycle instead of timing out.periodicCleanupTaskis only started when SSE is actually available — avoids a no-op interval running forever in polling-only mode.Behaviour after fix
subscribe/*access → unchanged behaviour (SSE +awaitIdsas before).subscribe/*→ polling-only mode: data loads vialistrequests, optimistic mutations persist immediately and are confirmed after the next poll.Repro
useLiveQuery.data === undefined)TimeoutWaitingForIdsError)