{"record":{"id":"8301889c5767fdc8","repo":"TanStack/query","slug":"argument-is-not-function","errorCode":null,"errorMessage":"argument is not function.","messagePattern":"argument is not function\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/query-async-storage-persister/src/asyncThrottle.ts","lineNumber":13,"sourceCode":"import { timeoutManager } from '@tanstack/query-core'\nimport { noop } from './utils'\n\ninterface AsyncThrottleOptions {\n  interval?: number\n  onError?: (error: unknown) => void\n}\n\nexport function asyncThrottle<TArgs extends ReadonlyArray<unknown>>(\n  func: (...args: TArgs) => Promise<void>,\n  { interval = 1000, onError = noop }: AsyncThrottleOptions = {},\n) {\n  if (typeof func !== 'function') throw new Error('argument is not function.')\n\n  let nextExecutionTime = 0\n  let lastArgs = null\n  let isExecuting = false\n  let isScheduled = false\n\n  return async (...args: TArgs) => {\n    lastArgs = args\n    if (isScheduled) return\n    isScheduled = true\n    while (isExecuting) {\n      await new Promise((done) => timeoutManager.setTimeout(done, interval))\n    }\n    while (Date.now() < nextExecutionTime) {\n      await new Promise((done) =>\n        timeoutManager.setTimeout(done, nextExecutionTime - Date.now()),\n      )\n    }","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/TanStack/query/blob/159982c80b844487054155c38ffc760fe4208daf/packages/query-async-storage-persister/src/asyncThrottle.ts#L1-L31","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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`."],"exampleFix":"// before\nconst throttled = asyncThrottle(persistFn, { interval: 1000 }) // persistFn is undefined\n// after\nif (typeof persistFn !== 'function') throw new TypeError('persistFn required')\nconst throttled = asyncThrottle(persistFn, { interval: 1000 })","handlingStrategy":"type-guard","validationCode":"function assertAsyncFunction(fn: unknown): asserts fn is (...a: any[]) => Promise<void> {\n  if (typeof fn !== 'function') throw new TypeError('asyncThrottle: first argument must be a function')\n}","typeGuard":"const isAsyncFn = (v: unknown): v is (...a: any[]) => Promise<void> =>\n  typeof v === 'function'","tryCatchPattern":null,"preventionTips":["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."],"tags":["persister","async-storage","runtime-guard","validation"],"backgroundTag":null,"analyzedSha":"159982c80b844487054155c38ffc760fe4208daf","analyzedAt":"2026-08-12T16:21:52.822Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}