Meituan-Dianping/mpvue · warning

Failed watching path: "${expOrFn}" Watcher only accepts simp

Error message

Failed watching path: "${expOrFn}" Watcher only accepts simple dot-delimited paths. For full control, use a function instead.

What it means

Warning from the Watcher constructor when the expression string passed as expOrFn cannot be parsed into a valid dot-delimited path (parsePath returns undefined). The watcher is created with a noop getter so it never fires, meaning the watch silently does nothing after this warning.

Source

Thrown at src/core/observer/watcher.js:77

    this.cb = cb
    this.id = ++uid // uid for batching
    this.active = true
    this.dirty = this.lazy // for lazy watchers
    this.deps = []
    this.newDeps = []
    this.depIds = new Set()
    this.newDepIds = new Set()
    this.expression = process.env.NODE_ENV !== 'production'
      ? expOrFn.toString()
      : ''
    // parse expression for getter
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = function () {}
        process.env.NODE_ENV !== 'production' && warn(
          `Failed watching path: "${expOrFn}" ` +
          'Watcher only accepts simple dot-delimited paths. ' +
          'For full control, use a function instead.',
          vm
        )
      }
    }
    this.value = this.lazy
      ? undefined
      : this.get()
  }

  /**
   * Evaluate the getter, and re-collect dependencies.
   */
  get () {
    pushTarget(this)
    let value

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Fix the path to a simple dot-delimited string like 'a.b.c'
  2. Pass a function instead: watch the result of () => this.a[this.key].b for complex access
  3. Validate/trim the dynamic path string before constructing the watcher
  4. If the target may not exist, use a function returning the nested value safely

Example fix

// before
this.$watch('form.fields[' + i + '].value!', cb) // unparseable
// after
this.$watch(function () { return this.form.fields[i] && this.form.fields[i].value }, cb)
Defensive patterns

Strategy: validation

Validate before calling

const SIMPLE_PATH_RE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/
function isValidWatchPath(p) { return typeof p === 'string' && SIMPLE_PATH_RE.test(p) }
// if (!isValidWatchPath(p)) pass a function instead

Type guard

function isWatcherSource(s) { return typeof s === 'function' || isValidWatchPath(s) }

Prevention

When it happens

Trigger: new Watcher(vm, 'a["b".c', ...) or any watch source with syntax parsePath cannot handle (brackets, invalid chars); watch: { 'items.': fn } typo; passing a path like 'a..b' or an expression like 'a + b' where a function is required; undefined/null passed as expOrFn.

Common situations: Typos in watch keys with trailing dots or stray brackets; using computed expressions in string form instead of a function; programmatic watcher creation from dynamic strings built at runtime.

Related errors


AI-assisted analysis of Meituan-Dianping/mpvue@6c5d78ee04 (2026-09-02). Data as JSON: /api/errors/09ae92aa99c96bf9. Report an issue: GitHub.