Describe the bug
When using a custom timeout provider, timer IDs can legally be 0. However,
the codebase uses truthy checks before calling clearTimeout /
clearInterval. Since 0 is falsy, timers with ID 0
are never cleared.
This leads to stale timers continuing to run, causing unexpected refetches,
extra updates, and lifecycle inconsistencies.
Custom timeout provider that returns 0:
// Custom timeout provider
let firstTimeout = true
let firstInterval = true
const timeoutProvider = {
setTimeout: (fn, ms) => {
if (firstTimeout) {
firstTimeout = false
return 0
}
return window.setTimeout(fn, ms)
},
clearTimeout: (id) => {
console.log('clearTimeout called with:', id)
window.clearTimeout(id)
},
setInterval: (fn, ms) => {
if (firstInterval) {
firstInterval = false
return 0
}
return window.setInterval(fn, ms)
},
clearInterval: (id) => {
console.log('clearInterval called with:', id)
window.clearInterval(id)
}
}
Use this provider with QueryClient / QueryObserver and trigger cleanup
(e.g., unsubscribe or option update). Timers with ID 0 will not be cleared.
Timers should always be cleared regardless of their numeric value.
Even when the timer ID is 0, cleanup functions should be called:
clearTimeout(0)
clearInterval(0)
Actual behavior
Timers with ID 0 are not cleared because of truthy checks like:
if (this.#gcTimeout) {
clearTimeout(this.#gcTimeout)
}
Since 0 is falsy, the cleanup logic is skipped.
Proposed fix
Replace truthy checks with explicit undefined checks:
// Before
if (this.#gcTimeout)
// After
if (this.#gcTimeout !== undefined)
Apply similar fixes to:
this.#staleTimeoutId
this.#refetchIntervalId
Your minimal, reproducible example
Replace all truthy timer checks (e.g., if (timerId)) with explicit undefined checks (e.g., if (timerId !== undefined)) so timer ID 0 is handled correctly.
Steps to reproduce
- Create a custom timeout provider where
setTimeout or setInterval returns 0
- Use it with
QueryClient / QueryObserver
- Configure
staleTime or refetchInterval so timers are scheduled
- Trigger cleanup (unsubscribe or update options)
- Observe that
clearTimeout / clearInterval are not called for ID 0
Expected behavior
Timers should always be cleared regardless of their numeric value.
Even when the timer ID is 0, cleanup functions should be called:
clearTimeout(0)
clearInterval(0)
How often does this bug happen?
None
Screenshots or Videos
No response
Platform
- OS: Windows
- Browser: Chrome, Edge
- Runtime: Node.js
Tanstack Query adapter
None
TanStack Query version
5.96.2
TypeScript version
No response
Additional context
No response
Describe the bug
When using a custom timeout provider, timer IDs can legally be
0. However, the codebase uses truthy checks before callingclearTimeout/clearInterval. Since0is falsy, timers with ID0are never cleared.This leads to stale timers continuing to run, causing unexpected refetches, extra updates, and lifecycle inconsistencies.
Custom timeout provider that returns
0:Use this provider with QueryClient / QueryObserver and trigger cleanup (e.g., unsubscribe or option update). Timers with ID
0will not be cleared.Timers should always be cleared regardless of their numeric value. Even when the timer ID is
0, cleanup functions should be called:Actual behavior
Timers with ID
0are not cleared because of truthy checks like:Since
0is falsy, the cleanup logic is skipped.Proposed fix
Replace truthy checks with explicit
undefinedchecks:Apply similar fixes to:
this.#staleTimeoutIdthis.#refetchIntervalIdYour minimal, reproducible example
Replace all truthy timer checks (e.g., if (timerId)) with explicit undefined checks (e.g., if (timerId !== undefined)) so timer ID 0 is handled correctly.
Steps to reproduce
setTimeoutorsetIntervalreturns0QueryClient/QueryObserverstaleTimeorrefetchIntervalso timers are scheduledclearTimeout/clearIntervalare not called for ID0Expected behavior
Timers should always be cleared regardless of their numeric value. Even when the timer ID is
0, cleanup functions should be called:How often does this bug happen?
None
Screenshots or Videos
No response
Platform
Tanstack Query adapter
None
TanStack Query version
5.96.2
TypeScript version
No response
Additional context
No response