mobxjs/mobx · info

[mobx.spy] Is a no-op in production builds

Error message

[mobx.spy] Is a no-op in production builds

What it means

spy() registers a global listener for MobX transition events, but spy instrumentation is compiled out of production builds. In a non-__DEV__ bundle the function is a no-op that only console.warns and returns an empty disposer. It exists to tell developers why their spy listener never receives events in production.

Solutions

  1. Call spy() only in development builds, guarded by process.env.NODE_ENV !== 'production'.
  2. If you need spy events in production, build against a development MobX bundle (not recommended for perf/size).
  3. Replace production logging with trace()/onBecomeObserved on specific observables, which are not spy-gated.
  4. Ignore the warning if the spy was best-effort debugging; the returned no-op disposer is safe to call.

Example fix

// before
import { spy } from 'mobx'
spy(event => console.log(event))

// after
import { spy } from 'mobx'
if (process.env.NODE_ENV !== 'production') {
  spy(event => console.log(event))
}
Defensive patterns

Strategy: fallback

Validate before calling

function spySupported() {
  return process.env.NODE_ENV !== 'production'
}

Prevention

When it happens

Trigger: Calling spy(listener) (or mobx.spy) in an app bundled with NODE_ENV=production / a minified production MobX build, so __DEV__ is false and the early-return branch runs.

Common situations: Debugging tooling (e.g. mobx-devtools) silently inert in production; logging wrappers that call spy unconditionally; testing spy-based instrumentation against production builds of the library.

Related errors


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

Appendix: source

Thrown at packages/mobx/src/core/spy.ts:63

    spyReport(change)
}

const END_EVENT: SpyEvent = { type: "report-end", spyReportEnd: true }

export function spyReportEnd(change?: { time?: number }) {
    if (!__DEV__) {
        return
    }
    if (change) {
        spyReport(assign({}, change, { type: "report-end" as const, spyReportEnd: true as const }))
    } else {
        spyReport(END_EVENT)
    }
}

export function spy(listener: (change: SpyEvent) => void): Lambda {
    if (!__DEV__) {
        console.warn(`[mobx.spy] Is a no-op in production builds`)
        return function () {}
    } else {
        globalState.spyListeners.push(listener)
        return once(() => {
            globalState.spyListeners = globalState.spyListeners.filter(l => l !== listener)
        })
    }
}

View on GitHub (pinned to 01211a698b)