mobxjs/mobx · warning

[mobx] Derivation '${derivation.name_}' is created/updated w

Error message

[mobx] Derivation '${derivation.name_}' is created/updated without reading any observable value.

What it means

warnAboutDerivationWithoutDependencies fires when a derivation (computed or reaction body) completes tracking without reading any observable. Because it depends on nothing it will never re-run, which is almost always a bug, so MobX warns when reactionRequiresObservable is globally enabled or the derivation has requiresObservable_ set.

Source

Thrown at packages/mobx/src/core/derivation.ts:215

    }
    return result
}

function warnAboutDerivationWithoutDependencies(derivation: IDerivation) {
    if (!__DEV__) {
        return
    }

    if (derivation.observing_.length !== 0) {
        return
    }

    if (
        typeof derivation.requiresObservable_ === "boolean"
            ? derivation.requiresObservable_
            : globalState.reactionRequiresObservable
    ) {
        console.warn(
            `[mobx] Derivation '${derivation.name_}' is created/updated without reading any observable value.`
        )
    }
}

/**
 * diffs newObserving with observing.
 * update observing to be newObserving with unique observables
 * notify observers that become observed/unobserved
 */
function bindDependencies(derivation: IDerivation) {
    // invariant(derivation.dependenciesState !== IDerivationState.NOT_TRACKING, "INTERNAL ERROR bindDependencies expects derivation.dependenciesState !== -1");
    const prevObserving = derivation.observing_
    const observing = (derivation.observing_ = derivation.newObserving_!)
    let lowestNewObservingDerivationState = IDerivationState_.UP_TO_DATE_

    // Go through all new observables and check diffValue: (this list can contain duplicates):
    //   0: first occurrence, change to 1 and keep it

View on GitHub (pinned to 01211a698b)

Solutions

  1. Make the derivation read at least one observable, or remove it if it needs no reactive inputs.
  2. Fix typos/wrong references so the body actually touches observable properties.
  3. Disable the global flag if intentional: configure({ reactionRequiresObservable: false }).
  4. For conditional bodies, hoist an observable read (e.g. store.ready) above the early return.
  5. Pass requiresObservable: true selectively only on derivations that must have deps.

Example fix

// before
autorun(() => {
  if (store.items.length === 0) return // fine, but reads nothing when guarded
  render(normalize(store.items))
})

// after
autorun(() => {
  const items = store.items // observable read first
  if (items.length === 0) return
  render(normalize(items))
})
Defensive patterns

Strategy: validation

Validate before calling

import { configure } from 'mobx'
// guard: every reaction body must read at least one observable
// configure({ reactionRequiresObservable: false }) // if legacy reactions read conditionally
function validateReactionBody(body) {
  const deps = new Set()
  const proxied = new Proxy(body, {})
  // simplest practical check: run under autorun with spy and assert an update event fires
  return typeof body === 'function' && body.length === 0
}

Prevention

When it happens

Trigger: Creating a reaction()/autorun() whose body reads no observables (e.g. reading plain JS fields, or reading before data is set up), or a computed that returns a constant, while configure({ reactionRequiresObservable: true }) or with requiresObservable: true on the decorator/observable options.

Common situations: Typo in property names inside a reaction body; reading from a plain (non-observable) object; reaction bodies guarded by an early return before any observable read; new teams enabling reactionRequiresObservable and hitting legacy reactions.

Related errors


AI-assisted analysis of mobxjs/mobx@01211a698b (2026-08-28). Data as JSON: /api/errors/2795d3b9b7c3248b. Report an issue: GitHub.