mobxjs/mobx · warning

[MobX] Since strict-mode is enabled, changing (observed) obs

Error message

[MobX] Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: ${atom.name_}

What it means

MobX strict mode (enforceActions) forbids mutating observed observables outside of an action. checkIfStateModificationsAreAllowed fires from mutators like set_, delete, addValue_, spliceWithArray_ and defineObservableProperty_ when globalState.allowStateChanges is false and the atom has observers or enforceActions is 'always'. It warns (console.warn) naming the observable via atom.name_ so you know which piece of state was mutated illegally.

Source

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

        }
    }
}

export function isComputingDerivation() {
    return globalState.trackingDerivation !== null // filter out actions inside computations
}

export function checkIfStateModificationsAreAllowed(atom: IAtom) {
    if (!__DEV__) {
        return
    }
    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.`
        )
    }
}

/**

View on GitHub (pinned to 01211a698b)

Solutions

  1. Wrap the mutation in runInAction(() => { ... }) at the mutation site.
  2. Move the mutation into an @action.bound method or action() wrapper on the store.
  3. For async flows, put every post-await assignment inside runInAction, since the action scope ends at the await.
  4. If mutations are intentional and safe, relax with configure({ enforceActions: 'never' }) — discouraged.
  5. Use makeAutoObservable with autoAction or flow() for generators doing async state updates.

Example fix

// before (enforceActions enabled)
async function loadUser() {
  const user = await fetchUser()
  store.user = user // warns: changing state outside action
}

// after
import { runInAction } from 'mobx'
async function loadUser() {
  const user = await fetchUser()
  runInAction(() => {
    store.user = user
  })
}
Defensive patterns

Strategy: validation

Validate before calling

import { configure, isAction } from 'mobx'
// ensure strict-mode config matches your mutation style:
// configure({ enforceActions: 'observed' })
function assertMutatedInsideAction(mutator) {
  if (!mutator._isMobxAction) {
    throw new Error('State mutation must be wrapped in an action (enforceActions is enabled)')
  }
  return mutator
}

Prevention

When it happens

Trigger: Calling observable.set(), array.push/splice, map.set/delete, or addObservable/addValue outside an action while configure({enforceActions: 'observed'|'always'}) is active and the atom has observers (or enforceActions==='always'). Also mutating state inside a computed or render, where allowStateChanges is false.

Common situations: Async callbacks (setTimeout, promise .then) updating store fields after an action ended; React event handlers writing directly to stores with enforceActions:'always'; migrating from MobX 4/5 defaults where mutations outside actions were allowed; forgetting runInAction after await inside an action.

Related errors


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