Meituan-Dianping/mpvue · warning

<select multiple v-model="${binding.expression}"> expects an

Error message

<select multiple v-model="${binding.expression}"> expects an Array value for its binding, but got ${Object.prototype.toString.call(value).slice(8, -1)}

What it means

For `<select multiple v-model>`, the v-model directive's setSelected routine expects the bound value to be an Array (one entry per selected option). When the binding value is any other type (string, number, object, undefined), Vue warns in development, naming the expression and the actual type via Object.prototype.toString, because option matching against the value would fail.

Source

Thrown at src/platforms/web/runtime/directives/model.js:73

      // in case the options rendered by v-for have changed,
      // it's possible that the value is out-of-sync with the rendered options.
      // detect such cases and filter out values that no longer has a matching
      // option in the DOM.
      const needReset = el.multiple
        ? binding.value.some(v => hasNoMatchingOption(v, el.options))
        : binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, el.options)
      if (needReset) {
        trigger(el, 'change')
      }
    }
  }
}

function setSelected (el, binding, vm) {
  const value = binding.value
  const isMultiple = el.multiple
  if (isMultiple && !Array.isArray(value)) {
    process.env.NODE_ENV !== 'production' && warn(
      `<select multiple v-model="${binding.expression}"> ` +
      `expects an Array value for its binding, but got ${
        Object.prototype.toString.call(value).slice(8, -1)
      }`,
      vm
    )
    return
  }
  let selected, option
  for (let i = 0, l = el.options.length; i < l; i++) {
    option = el.options[i]
    if (isMultiple) {
      selected = looseIndexOf(value, getValue(option)) > -1
      if (option.selected !== selected) {
        option.selected = selected
      }
    } else {
      if (looseEqual(getValue(option), value)) {

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Initialize the v-model binding as an empty array: `selected: []`.
  2. Normalize server/async data before assigning, e.g. `this.selected = Array.isArray(raw) ? raw : raw ? [raw] : []`.
  3. If a computed supplies the value, make sure every return path yields an array.
  4. When converting from single to multiple select, update the model shape alongside the template.

Example fix

// before
data: { selected: null }
<select multiple v-model="selected">...
// after
data: { selected: [] }
<select multiple v-model="selected">...
Defensive patterns

Strategy: type-guard

Validate before calling

if (!Array.isArray(form.selectedOptions)) {
  form.selectedOptions = []
}

Type guard

function isArrayBinding(v) {
  return Array.isArray(v)
}

Prevention

When it happens

Trigger: Binding a non-array to a multiple select: `v-model="selected"` where selected is initialized to `''`, `null`, a number, or an object; receiving server data where the field is a single value instead of a list; using a computed that sometimes returns a scalar.

Common situations: Form initialization before async data loads (value is undefined/null at componentUpdated time); switching a single-select to `multiple` without changing the model from string to array; API responses that return a comma-separated string for multi-select fields.

Related errors


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