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
- Wrap the mutation in runInAction(() => { ... }) at the mutation site.
- Move the mutation into an @action.bound method or action() wrapper on the store.
- For async flows, put every post-await assignment inside runInAction, since the action scope ends at the await.
- If mutations are intentional and safe, relax with configure({ enforceActions: 'never' }) — discouraged.
- 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
- Always wrap post-await assignments in runInAction.
- Use flow() for async generator-style actions.
- Make all store mutators @action.bound so call sites can't bypass actions.
- Keep enforceActions: 'observed' in dev to catch violations early.
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
- [mobx] Computed value '${this.name_}' is being read outside
- [MobX] Side effects like changing state are not allowed at t
- mobx-react-lite requires mobx at least version 7 to be avail
- mobx-react requires mobx to be available
- [mobx-react] Cannot read "${admin.name}.${key}" in a reactiv
AI-assisted analysis of mobxjs/mobx@01211a698b (2026-08-28).
Data as JSON: /api/errors/6e4b992f01522358.
Report an issue: GitHub.