TanStack/query · error
argument is not function.
Error message
argument is not function.
What it means
Runtime guard at the top of `asyncThrottle` in `@tanstack/query-async-storage-persister`. Despite the TypeScript signature requiring `func` to be a function, JavaScript callers (or transpiled/bundled code that lost the type check) can pass a non-function. The guard fails fast with a clear message instead of a cryptic `func is not a function` later.
Source
Thrown at packages/query-async-storage-persister/src/asyncThrottle.ts:13
import { timeoutManager } from '@tanstack/query-core'
import { noop } from './utils'
interface AsyncThrottleOptions {
interval?: number
onError?: (error: unknown) => void
}
export function asyncThrottle<TArgs extends ReadonlyArray<unknown>>(
func: (...args: TArgs) => Promise<void>,
{ interval = 1000, onError = noop }: AsyncThrottleOptions = {},
) {
if (typeof func !== 'function') throw new Error('argument is not function.')
let nextExecutionTime = 0
let lastArgs = null
let isExecuting = false
let isScheduled = false
return async (...args: TArgs) => {
lastArgs = args
if (isScheduled) return
isScheduled = true
while (isExecuting) {
await new Promise((done) => timeoutManager.setTimeout(done, interval))
}
while (Date.now() < nextExecutionTime) {
await new Promise((done) =>
timeoutManager.setTimeout(done, nextExecutionTime - Date.now()),
)
}View on GitHub (pinned to 159982c80b)
Solutions
- Confirm the value passed to `asyncThrottle` is genuinely a function at runtime with `console.log(typeof throttleFn)`.
- Provide a fallback or initialize the variable before calling: `const throttleFn = realFn ?? (async () => {})`.
- If importing from a barrel, verify the named export exists in the installed version.
- Add a unit test asserting `typeof throttleFn === 'function'` before invoking `asyncThrottle`.
Example fix
// before
const throttled = asyncThrottle(persistFn, { interval: 1000 }) // persistFn is undefined
// after
if (typeof persistFn !== 'function') throw new TypeError('persistFn required')
const throttled = asyncThrottle(persistFn, { interval: 1000 }) Defensive patterns
Strategy: type-guard
Validate before calling
function assertAsyncFunction(fn: unknown): asserts fn is (...a: any[]) => Promise<void> {
if (typeof fn !== 'function') throw new TypeError('asyncThrottle: first argument must be a function')
} Type guard
const isAsyncFn = (v: unknown): v is (...a: any[]) => Promise<void> => typeof v === 'function'
Prevention
- Type the persister callback strictly and let TypeScript enforce the function type.
- Initialize the callback before passing it; avoid conditional assignment that can leave undefined.
- Add a unit test that passes a non-function and expects the guard to fire.
- Verify named imports resolve in your bundler setup.
When it happens
Trigger: Passing `undefined`, `null`, a string, or an object as the first argument to `asyncThrottle`; destructuring a persister callback that was not yet assigned; calling `asyncThrottle(throttleFn)` where `throttleFn` is conditionally defined and resolved to a falsy non-function.
Common situations: Building a custom persister that wraps `asyncThrottle` and forwarding an uninitialized variable; migrating from a default export that changed shape; bundlers that tree-shake away a function reference; interop with CommonJS where the default import is wrapped.
Related errors
- Provided storage does not implement `entries` method. Garbag
- Provided storage does not implement `entries` method. Restor
- Provided storage does not implement `entries` method. Remova
- Bad argument type. Starting with v5, only the "Object" form
- ${this.queryHash} data is undefined
AI-assisted analysis of TanStack/query@159982c80b (2026-08-12).
Data as JSON: /api/errors/8301889c5767fdc8.
Report an issue: GitHub.