TanStack/query · error

Expected enabled to be a boolean or a callback that returns

Error message

Expected enabled to be a boolean or a callback that returns a boolean

What it means

Validation in `QueryObserver.setOptions` (`@tanstack/query-core`). `enabled` must be `undefined`, a `boolean`, a `() => boolean` function, or an object that `resolveQueryBoolean` can collapse to a boolean. Anything else (string, number, Promise, array, opaque object) is rejected to prevent silent misbehavior of the observer's fetch gating.

Source

Thrown at packages/query-core/src/queryObserver.ts:159

      TError,
      TData,
      TQueryData,
      TQueryKey
    >,
  ): void {
    const prevOptions = this.options
    const prevQuery = this.#currentQuery

    this.options = this.#client.defaultQueryOptions(options)

    if (
      this.options.enabled !== undefined &&
      typeof this.options.enabled !== 'boolean' &&
      typeof this.options.enabled !== 'function' &&
      typeof resolveQueryBoolean(this.options.enabled, this.#currentQuery) !==
        'boolean'
    ) {
      throw new Error(
        'Expected enabled to be a boolean or a callback that returns a boolean',
      )
    }

    this.#updateQuery()
    this.#currentQuery.setOptions(this.options)

    if (
      prevOptions._defaulted &&
      !shallowEqualObjects(this.options, prevOptions)
    ) {
      this.#client.getQueryCache().notify({
        type: 'observerOptionsUpdated',
        query: this.#currentQuery,
        observer: this,
      })
    }

View on GitHub (pinned to 159982c80b)

Solutions

  1. Coerce to boolean: `enabled: Boolean(myValue)` or `enabled: !!myValue`.
  2. Wrap dynamic resolution in a function: `enabled: () => computeEnabled()`.
  3. Unwrap framework signals before passing: `enabled: signal()` for Angular/Preact signals, `enabled: get(value)` for Svelte stores.
  4. Type the options object strictly so TypeScript rejects non-boolean/non-function values upstream.

Example fix

// before
useQuery({ queryKey, queryFn, enabled: mySignal })
// after
useQuery({ queryKey, queryFn, enabled: () => mySignal() })
Defensive patterns

Strategy: type-guard

Validate before calling

function normalizeEnabled(v: unknown): boolean | (() => boolean) | undefined {
  if (v === undefined) return undefined
  if (typeof v === 'boolean') return v
  if (typeof v === 'function') return v as () => boolean
  throw new Error('enabled must be boolean, () => boolean, or undefined')
}

Type guard

const isValidEnabled = (v: unknown): v is boolean | (() => boolean) | undefined =>
  v === undefined || typeof v === 'boolean' || typeof v === 'function'

Prevention

When it happens

Trigger: Passing `enabled: 'yes'`, `enabled: 1`, `enabled: someSignalObject` (where the signal is not a function), `enabled: { default: true }`, or a `Promise<boolean>`; spreading a config where `enabled` came from an untyped JSON source.

Common situations: Bridging Angular signals/Preact signals into `enabled` without unwrapping; passing a truthy non-boolean from a form field; receiving `enabled` from a query string or environment variable as a string; refactor that changed a boolean prop to an object.

Related errors


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