{"record":{"id":"f68931d2c5805809","repo":"Meituan-Dianping/mpvue","slug":"you-may-have-an-infinite-update-loop-in-watcher-wi","errorCode":null,"errorMessage":"You may have an infinite update loop in watcher with expression \"${watcher.expression}\" / in a component render function.","messagePattern":"You may have an infinite update loop in watcher with expression \"(.+?)\" / in a component render function\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/core/observer/scheduler.js","lineNumber":63,"sourceCode":"  //    created before the child)\n  // 2. A component's user watchers are run before its render watcher (because\n  //    user watchers are created before the render watcher)\n  // 3. If a component is destroyed during a parent component's watcher run,\n  //    its watchers can be skipped.\n  queue.sort((a, b) => a.id - b.id)\n\n  // do not cache length because more watchers might be pushed\n  // as we run existing watchers\n  for (index = 0; index < queue.length; index++) {\n    watcher = queue[index]\n    id = watcher.id\n    has[id] = null\n    watcher.run()\n    // in dev build, check and stop circular updates.\n    if (process.env.NODE_ENV !== 'production' && has[id] != null) {\n      circular[id] = (circular[id] || 0) + 1\n      if (circular[id] > MAX_UPDATE_COUNT) {\n        warn(\n          'You may have an infinite update loop ' + (\n            watcher.user\n              ? `in watcher with expression \"${watcher.expression}\"`\n              : `in a component render function.`\n          ),\n          watcher.vm\n        )\n        break\n      }\n    }\n  }\n\n  // keep copies of post queues before resetting state\n  const activatedQueue = activatedChildren.slice()\n  const updatedQueue = queue.slice()\n\n  resetSchedulerState()\n","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/Meituan-Dianping/mpvue/blob/6c5d78ee04f58c6d782c456dfcf0fe63c0b7f89b/src/core/observer/scheduler.js#L45-L81","documentation":"Dev-mode warning from the scheduler's flushSchedulerQueue when the same watcher is re-queued and run more than MAX_UPDATE_COUNT (100) times in one flush cycle. It indicates a reactive update loop: a watcher or render function mutates state that triggers itself again.","triggerScenarios":"A watcher whose handler sets the same value it watches; a computed that both reads and writes state consumed by the render; a component render function mutating reactive data used in the template (e.g. calling a method that increments during render); two watchers updating each other in a cycle.","commonSituations":"v-model on a computed without a setter guard; parent-child prop/event ping-pong where each side writes on change; watchers with deep:true on large mutating structures; side effects in computed getters.","solutions":["Find the watcher expression named in the warning and remove its self-triggering write","Move mutations out of render/computed getters into methods or lifecycle hooks","Guard the watcher: only assign when the value actually changed","Break parent-child cycles by emitting events upward instead of writing props back","Use a debounce/nextTick for cascading updates"],"exampleFix":"// before\nwatch: { value(v) { this.value = v.trim() } } // re-triggers itself\n// after\ncomputed: {\n  trimmed: {\n    get() { return this.value },\n    set(v) { this.$emit('input', v.trim()) }\n  }\n}","handlingStrategy":"validation","validationCode":"function watcherWritesWatchedKey(watchers, computedKeys) {\n  const offenders = []\n  for (const [expr, def] of Object.entries(watchers || {})) {\n    const handler = typeof def === 'function' ? def : def && def.handler\n    if (handler && String(handler).includes('this.' + expr)) offenders.push(expr)\n  }\n  return offenders\n}","typeGuard":"function isAcyclicWatcher(expr, handler) { return !String(handler).includes('this.' + expr) }","tryCatchPattern":null,"preventionTips":["Never mutate the watched value inside its own handler","Keep computed getters pure; do side effects in methods/watchers","Avoid parent-child prop/event write cycles; emit upward only","Add value-change guards in watchers before assigning"],"tags":["vue","reactivity","infinite-loop","watcher","scheduler"],"backgroundTag":"infinite-update-loop","analyzedSha":"6c5d78ee04f58c6d782c456dfcf0fe63c0b7f89b","analyzedAt":"2026-09-02T05:17:40.946Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T11:17:12.671Z"}