fix: run reconcileSubscription after every fetch attempt - #27
Merged
Conversation
Reactive getConfig() switching subscribe to undefined on an error response (e.g. poll only while response.ok) silently failed when the error body did not match the result entity shape. runQuery only called reconcileSubscription after applyData succeeded, so parseEntities throwing on the body shape skipped the reconcile, the inner reactiveSignal never re-read this.config, and the running poll subscriber kept ticking against stale config. Move the call into a finally block so it fires after every fetch attempt. On adapter-level failures where ctx.response was never updated this is a no-op: the cached config is returned and the ref check inside reconcileSubscription short-circuits. The existing poll.test "stops polling when getConfig() switches subscribe to undefined after an error" is rewritten to use t.entity() with an unparseable 404 body, which fails on main and passes with this change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pzuraq
approved these changes
May 18, 2026
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.
Summary
Reactive
getConfig()that switchessubscribetoundefinedon an error response (e.g. poll only whileresponse.ok) silently failed when the error body did not match the result entity shape.runQueryonly calledreconcileSubscriptionafterapplyDatasucceeded (QueryResult.ts:337), soparseEntitiesthrowing on the body shape skipped the reconcile, the innerreactiveSignalnever re-readthis.config, and the running poll subscriber kept ticking against stale config.Fix
Move the
reconcileSubscriptioncall into afinallyblock so it fires after every fetch attempt, regardless of whether parsing succeeds.return withRetry( async () => { - const freshData = await adapter.send(ctx, signal); - this.updatedAt = Date.now(); - - const result = this.applyData(freshData, true); - this.saveQueryMetadata(); - - this.reconcileSubscription(); - - return result; + try { + const freshData = await adapter.send(ctx, signal); + this.updatedAt = Date.now(); + + const result = this.applyData(freshData, true); + this.saveQueryMetadata(); + + return result; + } finally { + this.reconcileSubscription(); + } },On adapter-level failures where
ctx.responsewas never updated this is a no-op:_resolvedOptionsreturns its cached value, the subscribe ref hasn't changed, and the ref check insidereconcileSubscriptionshort-circuits.Why the existing test missed this
stops polling when getConfig() switches subscribe to undefined after an errorusedresult = { n: t.number }with a 404 body of{ n: callCount }. The body matched the result schema,applyDatasucceeded, and the reconcile path ran normally. The test passed even though the bug was present for the entity-shape-mismatch case.That test is rewritten to use
t.entity()with a 404 body that does not match the entity shape ({error: 'Not found'}). It fails onmainand passes with this change.Test plan
🤖 Generated with Claude Code