Meituan-Dianping/mpvue · error
Cannot find element: ${el}
Error message
Cannot find element: ${el} What it means
Vue's query() util resolves the mount target: when el is a string selector it calls document.querySelector. If nothing matches, Vue warns 'Cannot find element: <selector>' and substitutes an empty detached <div>, so the app mounts into a throwaway node instead of the intended element (usually nothing becomes visible).
Source
Thrown at src/platforms/web/util/index.js:16
/* @flow */
import { warn } from 'core/util/index'
export * from './attrs'
export * from './class'
export * from './element'
/**
* Query an element selector if it's not an element already.
*/
export function query (el: string | Element): Element {
if (typeof el === 'string') {
const selected = document.querySelector(el)
if (!selected) {
process.env.NODE_ENV !== 'production' && warn(
'Cannot find element: ' + el
)
return document.createElement('div')
}
return selected
} else {
return el
}
}
View on GitHub (pinned to 6c5d78ee04)
Solutions
- Move the script to the end of <body> or add defer, or wrap mount in DOMContentLoaded so the target exists first.
- Verify the selector string matches an existing element id/class exactly (case-sensitive ids).
- Mount to document.body or an explicitly created element if the target should be generated.
- Check that no other script removes or replaces the target element before mounting.
Example fix
// before
// <head> inline script
new Vue({ el: '#app', render: h => h(App) })
// after
document.addEventListener('DOMContentLoaded', () => {
new Vue({ el: '#app', render: h => h(App) })
}) Defensive patterns
Strategy: validation
Validate before calling
function requireMountTarget(selector) {
const el = document.querySelector(selector)
if (!el) throw new Error('Mount target not found: ' + selector)
return el
}
// new Vue({ el: requireMountTarget('#app') ... }) — throws before Vue substitutes a dummy div Type guard
function elementExists(el) {
return typeof el === 'string' ? document.querySelector(el) !== null : el instanceof Element
} Try / catch
try {
new Vue({ el: '#app', render: h => h(App) })
} catch (e) {
console.error('Mount failed:', e.message)
} Prevention
- Load app scripts with defer or at the end of <body>.
- Wrap mounting in DOMContentLoaded when scripts load early.
- Double-check selector spelling and case against the actual DOM.
- Never replace/remove the mount container before initializing the app.
When it happens
Trigger: new Vue({ el: '#app' }) (or $mount('#app')) where no element with that selector exists in the DOM at call time — the script ran before the DOM was parsed, the id/class is misspelled, or the element is inside a not-yet-rendered section.
Common situations: Loading a bundle in <head> without defer/DOMContentLoaded; typo in the selector; mounting into an element added later by another script; SPA mounted into a container removed by a prior framework; SSR/hydration mismatches where the element is absent.
Related errors
- Do not mount Vue to <html> or <body> - mount to normal eleme
- invalid template option:${template}
- tag <${tag}> has no matching end tag.
- [Vue tip]: ${msg}
- passive and prevent can't be used together. Passive handler
AI-assisted analysis of Meituan-Dianping/mpvue@6c5d78ee04 (2026-09-02).
Data as JSON: /api/errors/0613a0c8893c9840.
Report an issue: GitHub.