mobxjs/mobx · warning

[MobX] Side effects like changing state are not allowed at t

Error message

[MobX] 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_}

What it means

Same checkIfStateModificationsAreAllowed guard, but this variant of the message is emitted when globalState.enforceActions is falsy while the modification still happens where state changes are disallowed — typically inside a computed value or during a React render. MobX blocks side effects during derivation/rendering because changing observed state there breaks the reactive transaction and can cause inconsistent renders.

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. Move the state write out of the computed/render path into an action, reaction, or event handler.
  2. If a computed must record something, model it as a true computed (getObservableValue) instead of writing back.
  3. Use runInAction inside an event/async callback rather than during rendering.
  4. If the write must happen lazily, use an @observable with a setter method invoked from user code, not render.

Example fix

// before
get fullName() {
  this.displayName = this.first + ' ' + this.last // side effect in computed
  return this.displayName
}

// after
get fullName() {
  return this.first + ' ' + this.last // pure derivation
}
Defensive patterns

Strategy: validation

Validate before calling

function assertNotInRender(mutate) {
  if (typeof window !== 'undefined' && window.__MOBX_IN_RENDER__) {
    throw new Error('Cannot mutate state during render; defer to an action/event handler')
  }
  return mutate
}

Prevention

When it happens

Trigger: Writing to an observable from inside a computed() getter, an observer component's render body, or a reaction's trace phase, when allowStateChanges is false but enforceActions is not 'always' (message takes the 'side effects' branch because globalState.enforceActions is falsy/0).

Common situations: Caching derived values back into a store property inside a computed getter; calling store mutators directly from JSX render code; initializing observers lazily during first render; upgrading code that relied on enforceActions defaults.

Related errors


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