{"record":{"id":"09ae92aa99c96bf9","repo":"Meituan-Dianping/mpvue","slug":"failed-watching-path-exporfn-watcher-only-ac","errorCode":null,"errorMessage":"Failed watching path: \"${expOrFn}\" Watcher only accepts simple dot-delimited paths. For full control, use a function instead.","messagePattern":"Failed watching path: \"(.+?)\" Watcher only accepts simple dot-delimited paths\\. For full control, use a function instead\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/core/observer/watcher.js","lineNumber":77,"sourceCode":"    this.cb = cb\n    this.id = ++uid // uid for batching\n    this.active = true\n    this.dirty = this.lazy // for lazy watchers\n    this.deps = []\n    this.newDeps = []\n    this.depIds = new Set()\n    this.newDepIds = new Set()\n    this.expression = process.env.NODE_ENV !== 'production'\n      ? expOrFn.toString()\n      : ''\n    // parse expression for getter\n    if (typeof expOrFn === 'function') {\n      this.getter = expOrFn\n    } else {\n      this.getter = parsePath(expOrFn)\n      if (!this.getter) {\n        this.getter = function () {}\n        process.env.NODE_ENV !== 'production' && warn(\n          `Failed watching path: \"${expOrFn}\" ` +\n          'Watcher only accepts simple dot-delimited paths. ' +\n          'For full control, use a function instead.',\n          vm\n        )\n      }\n    }\n    this.value = this.lazy\n      ? undefined\n      : this.get()\n  }\n\n  /**\n   * Evaluate the getter, and re-collect dependencies.\n   */\n  get () {\n    pushTarget(this)\n    let value","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/Meituan-Dianping/mpvue/blob/6c5d78ee04f58c6d782c456dfcf0fe63c0b7f89b/src/core/observer/watcher.js#L59-L95","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the path to a simple dot-delimited string like 'a.b.c'","Pass a function instead: watch the result of () => this.a[this.key].b for complex access","Validate/trim the dynamic path string before constructing the watcher","If the target may not exist, use a function returning the nested value safely"],"exampleFix":"// before\nthis.$watch('form.fields[' + i + '].value!', cb) // unparseable\n// after\nthis.$watch(function () { return this.form.fields[i] && this.form.fields[i].value }, cb)","handlingStrategy":"validation","validationCode":"const SIMPLE_PATH_RE = /^[A-Za-z_$][\\w$]*(\\.[A-Za-z_$][\\w$]*)*$/\nfunction isValidWatchPath(p) { return typeof p === 'string' && SIMPLE_PATH_RE.test(p) }\n// if (!isValidWatchPath(p)) pass a function instead","typeGuard":"function isWatcherSource(s) { return typeof s === 'function' || isValidWatchPath(s) }","tryCatchPattern":null,"preventionTips":["Use only plain dot paths for string watch sources","Prefer functions for dynamic or computed access paths","Lint watch keys for typos like trailing dots or brackets"],"tags":["vue","watcher","path-parsing","reactivity"],"backgroundTag":"invalid-watch-path","analyzedSha":"6c5d78ee04f58c6d782c456dfcf0fe63c0b7f89b","analyzedAt":"2026-09-02T05:17:40.946Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T11:17:12.671Z"}