Meituan-Dianping/mpvue · error

Avoid using observed data object as vnode data: ${JSON.strin

Error message

Avoid using observed data object as vnode data: ${JSON.stringify(data)}
Always create fresh vnode data objects in each render!

What it means

Vue's virtual DOM patches by comparing vnode data objects. If the `data` argument passed to createElement (or h) is a reactive, observed object (it has an `__ob__` property), Vue refuses to use it because mutating/observing shared data across renders corrupts the patching process. It warns and renders an empty vnode instead, so the element silently disappears.

Source

Thrown at src/core/vdom/create-element.js:53

    normalizationType = children
    children = data
    data = undefined
  }
  if (isTrue(alwaysNormalize)) {
    normalizationType = ALWAYS_NORMALIZE
  }
  return _createElement(context, tag, data, children, normalizationType)
}

export function _createElement (
  context: Component,
  tag?: string | Class<Component> | Function | Object,
  data?: VNodeData,
  children?: any,
  normalizationType?: number
): VNode {
  if (isDef(data) && isDef((data: any).__ob__)) {
    process.env.NODE_ENV !== 'production' && warn(
      `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
      'Always create fresh vnode data objects in each render!',
      context
    )
    return createEmptyVNode()
  }
  // object syntax in v-bind
  if (isDef(data) && isDef(data.is)) {
    tag = data.is
  }
  if (!tag) {
    // in case of component :is set to falsy value
    return createEmptyVNode()
  }
  // warn against non-primitive key
  if (process.env.NODE_ENV !== 'production' &&
    isDef(data) && isDef(data.key) && !isPrimitive(data.key)
  ) {

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Clone the object so it is plain: `h('div', { ...data })`
  2. Recreate the vnode data object inline in the render function on every call
  3. Strip reactivity with `JSON.parse(JSON.stringify(data))` if spreading is not enough (e.g. nested observed objects)
  4. If the object is intentionally static, freeze it before Vue observes it: `Object.freeze(data)`

Example fix

// before
render(h) {
  return h('div', this.rowData) // rowData is reactive
}
// after
render(h) {
  return h('div', { ...this.rowData })
}
Defensive patterns

Strategy: validation

Validate before calling

function isObservedData (data) {
  return data != null && typeof data === 'object' && '__ob__' in data
}
// before calling h():
if (isObservedData(data)) data = { ...data }

Type guard

function isPlainObject (v) {
  if (v === null || typeof v !== 'object') return false
  const proto = Object.getPrototypeOf(v)
  return proto === Object.prototype || proto === null
}

Prevention

When it happens

Trigger: Calling `h(tag, someReactiveDataObject, children)` where the data object came from `data()` state, props, or a store getter, e.g. `h('div', this.attrsFromState)` in a render function or functional component.

Common situations: Spreading reactive component state into vnode data; reusing a cached vnode-data object stored in Vuex/redux-like state; programmatically building vnodes from observed API responses.

Related errors


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