mobxjs/mobx · warning

[mobx] Observable '${observable.name_}' being read outside a

Error message

[mobx] Observable '${observable.name_}' being read outside a reactive context.

What it means

checkIfStateReadsAreAllowed warns when an observable marked as requiring reaction (observableRequiresReaction enabled, or per-observable requiresObservable_) is read outside any reactive context (no active derivation). MobX enforces this so reads that should drive reactions aren't silently untracked, which would break the reaction contract.

Source

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

    const hasObservers = !!atom.observers_ && atom.observers_.size > 0
    // Should not be possible to change observed state outside strict mode, except during initialization, see #563
    if (
        !globalState.allowStateChanges &&
        (hasObservers || globalState.enforceActions === "always")
    ) {
        console.warn(
            "[MobX] " +
                (globalState.enforceActions
                    ? "Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: "
                    : "Side effects like changing state are not allowed at this point. Are you trying to modify state from, for example, a computed value or the render function of a React component? You can wrap side effects in 'runInAction' (or decorate functions with 'action') if needed. Tried to modify: ") +
                atom.name_
        )
    }
}

export function checkIfStateReadsAreAllowed(observable: IObservable) {
    if (__DEV__ && !globalState.allowStateReads && globalState.observableRequiresReaction) {
        console.warn(
            `[mobx] Observable '${observable.name_}' being read outside a reactive context.`
        )
    }
}

/**
 * Executes the provided function `f` and tracks which observables are being accessed.
 * The tracking information is stored on the `derivation` object and the derivation is registered
 * as observer of any of the accessed observables.
 */
export function trackDerivedFunction<T>(derivation: IDerivation, f: () => T, context: any) {
    const prevAllowStateReads = __DEV__ ? allowStateReadsStart(true) : true
    changeDependenciesStateTo0(derivation)
    // Preallocate array; will be trimmed by bindDependencies.
    derivation.newObserving_ = new Array(
        // Reserve constant space for initial dependencies, dynamic space otherwise.
        // See https://github.com/mobxjs/mobx/pull/3833
        derivation.runId_ === 0 ? 100 : derivation.observing_.length

View on GitHub (pinned to 01211a698b)

Solutions

  1. Read the value inside a reactive context: computed, reaction(), autorun(), or an observer component.
  2. If the read is legitimately one-off (e.g. logging), disable the flag locally or globally: configure({ observableRequiresReaction: false }).
  3. Remove requiresObservable: true from observables that are read imperatively by design.
  4. Use toJS() in non-reactive utility code if you just need a snapshot.

Example fix

// before (observableRequiresReaction: true)
function logCount(store) {
  console.log(store.count) // read outside reactive context
}

// after
import { autorun } from 'mobx'
autorun(() => console.log(store.count))
Defensive patterns

Strategy: validation

Validate before calling

import { _getGlobalState, configure } from 'mobx'
// ensure reads happen in reactive contexts when this flag is on:
// configure({ observableRequiresReaction: false }) // for imperative utility code
function canReadReactively() {
  return _getGlobalState().trackingDerivation !== null || !_getGlobalState().observableRequiresReaction
}

Prevention

When it happens

Trigger: Reading an observable's .get() or property value in plain imperative code (not inside computed/reaction/observer render) while configure({ observableRequiresReaction: true }) is set, or the observable was created with requiresObservable: true (e.g. via options on observable/annotation).

Common situations: Debugging in the console with observableRequiresReaction left on; reading store values inside plain utility functions called outside renderers; unit tests reading observables directly; onBecomeObserved-style code paths invoked outside tracking.

Related errors


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