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

  1. Confirm the value passed to `asyncThrottle` is genuinely a function at runtime with `console.log(typeof throttleFn)`.
  2. Provide a fallback or initialize the variable before calling: `const throttleFn = realFn ?? (async () => {})`.
  3. If importing from a barrel, verify the named export exists in the installed version.
  4. 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

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


AI-assisted analysis of TanStack/query@159982c80b (2026-08-12). Data as JSON: /api/errors/8301889c5767fdc8. Report an issue: GitHub.