Meituan-Dianping/mpvue · warning

tag <${tag}> has no matching end tag.

Error message

tag <${tag}> has no matching end tag.

What it means

This dev-mode warning is emitted by the HTML parser's parseEndTag when a closing tag does not match any open element in the parser's element stack. The parser closes every remaining element above the mismatch point via options.end, and warns for each one left without a matching end tag. It indicates malformed template markup (unclosed or wrongly nested tags).

Source

Thrown at packages/weex-template-compiler/build.js:520

    if (tagName) {
      for (pos = stack.length - 1; pos >= 0; pos--) {
        if (stack[pos].lowerCasedTag === lowerCasedTagName) {
          break
        }
      }
    } else {
      // If no tag name is provided, clean shop
      pos = 0;
    }

    if (pos >= 0) {
      // Close all the open elements, up the stack
      for (var i = stack.length - 1; i >= pos; i--) {
        if (process.env.NODE_ENV !== 'production' &&
          (i > pos || !tagName) &&
          options.warn
        ) {
          options.warn(
            ("tag <" + (stack[i].tag) + "> has no matching end tag.")
          );
        }
        if (options.end) {
          options.end(stack[i].tag, start, end);
        }
      }

      // Remove the open elements from the stack
      stack.length = pos;
      lastTag = pos && stack[pos - 1].tag;
    } else if (lowerCasedTagName === 'br') {
      if (options.start) {
        options.start(tagName, [], true, start, end);
      }
    } else if (lowerCasedTagName === 'p') {
      if (options.start) {
        options.start(tagName, [], false, start, end);

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Add the missing end tag for the element named in the warning
  2. Reorder the end tags so nesting matches the start tags
  3. Validate the template HTML in an editor/linter before compiling

Example fix

// before
<div><span>hi</div>
// after
<div><span>hi</span></div>
Defensive patterns

Strategy: validation

Validate before calling

function checkBalancedTags(template) {
  const voidTags = new Set(['area','base','br','col','embed','hr','img','input','link','meta','param','source','track','wbr']);
  const stack = [];
  const re = /<\/?([a-zA-Z][\w-]*)[^>]*?(\/?)>/g;
  let m;
  while ((m = re.exec(template))) {
    const [full, tag, selfClose] = m;
    if (full.startsWith('</')) {
      if (stack.pop() !== tag) return `mismatched end tag </${tag}>`;
    } else if (!selfClose && !voidTags.has(tag.toLowerCase())) {
      stack.push(tag);
    }
  }
  return stack.length ? `unclosed tag <${stack[stack.length-1]}>` : null;
}

Type guard

function isTemplateBalanced(template) {
  return typeof template === 'string' && checkBalancedTags(template) === null;
}

Prevention

When it happens

Trigger: Compiling a template where an element is never closed, or end tags appear out of nesting order, so parseEndTag finds no stack entry matching the closing tag name (warn fires when i > pos or tagName is empty and options.warn is set).

Common situations: Hand-written templates with <div> opened but not closed, self-closing a non-void tag incorrectly, or JSX-like nesting mistakes; often after copy-pasting markup or refactoring without closing a wrapper element.

Related errors


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