Meituan-Dianping/mpvue · warning

<transition> explicit ${name} duration is NaN - the duration

Error message

<transition> explicit ${name} duration is NaN - the duration expression might be incorrect.

What it means

This is the NaN branch of checkDuration: the duration prop on <transition> is a number type but evaluates to NaN, which usually means the duration expression is computed incorrectly (e.g. Number('abc'), undefined arithmetic). NaN durations make transition timing impossible, so Vue warns in development.

Source

Thrown at src/platforms/web/runtime/modules/transition.js:287

      })
    }
    leave && leave(el, cb)
    if (!expectsCSS && !userWantsControl) {
      cb()
    }
  }
}

// only used in dev mode
function checkDuration (val, name, vnode) {
  if (typeof val !== 'number') {
    warn(
      `<transition> explicit ${name} duration is not a valid number - ` +
      `got ${JSON.stringify(val)}.`,
      vnode.context
    )
  } else if (isNaN(val)) {
    warn(
      `<transition> explicit ${name} duration is NaN - ` +
      'the duration expression might be incorrect.',
      vnode.context
    )
  }
}

function isValidDuration (val) {
  return typeof val === 'number' && !isNaN(val)
}

/**
 * Normalize a transition hook's argument length. The hook may be:
 * - a merged hook (invoker) with the original in .fns
 * - a wrapped component method (check ._length)
 * - a plain function (.length)
 */
function getHookArgumentsLength (fn: Function): boolean {

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Fix the expression producing the duration so it yields a real number (validate input before Number()/parseFloat).
  2. Guard against undefined: use :duration="Number(str) || 300" with a sane default.
  3. If parsing '300ms' style strings, strip units first: parseFloat('300ms').
  4. Log the value in dev to find where NaN originates.

Example fix

// before
<transition :duration="parseFloat(this.durationStr)">...</transition>
// after
<transition :duration="parseFloat(this.durationStr) || 300">...</transition>
Defensive patterns

Strategy: validation

Validate before calling

function toDuration(v, fallback = 300) {
  const n = typeof v === 'string' ? parseFloat(v) : Number(v)
  return Number.isFinite(n) ? n : fallback
}
// use: :duration="toDuration(rawDuration)"

Type guard

function isRealNumber(v) {
  return typeof v === 'number' && Number.isFinite(v)
}

Prevention

When it happens

Trigger: Passing a computed duration to <transition> (or explicit enter/leave hooks) whose value is a number-typed NaN — e.g. :duration="parseFloat(str)" where str is not numeric, or arithmetic like this.delay * 2 when this.delay is undefined.

Common situations: Parsing strings from attributes or config without validating; uninitialized reactive data used in the expression; a computed that divides by zero or multiplies undefined; unit strings like '300ms' fed to Number().

Related errors


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