vuejs/vue-router · info
Failed to resolve async component ${key}: ${reason}
Error message
Failed to resolve async component ${key}: ${reason} What it means
This is a dev-only warn (console.warn) shown when <router-link> is used with a scoped slot (v-slot) but without the custom prop. In Vue Router 4 the default anchor rendering will change for slot content, so the library warns you to opt in with custom to take full control of the rendered element. It fires once per session (warnedCustomSlot flag).
Source
Thrown at src/util/resolve-components.js:40
const resolve = once(resolvedDef => {
if (isESModule(resolvedDef)) {
resolvedDef = resolvedDef.default
}
// save resolved on async factory in case it's used elsewhere
def.resolved = typeof resolvedDef === 'function'
? resolvedDef
: _Vue.extend(resolvedDef)
match.components[key] = resolvedDef
pending--
if (pending <= 0) {
next()
}
})
const reject = once(reason => {
const msg = `Failed to resolve async component ${key}: ${reason}`
process.env.NODE_ENV !== 'production' && warn(false, msg)
if (!error) {
error = isError(reason)
? reason
: new Error(msg)
next(error)
}
})
let res
try {
res = def(resolve, reject)
} catch (e) {
reject(e)
}
if (res) {
if (typeof res.then === 'function') {
res.then(resolve, reject)
} else {View on GitHub (pinned to 680ccc68c5)
Solutions
- Add the custom prop to <router-link> when using v-slot and render your own <a>.
- If you want the default <a> rendering, drop the scoped slot and use the default slot instead.
- Suppress by acknowledging the future behavior change if it is intentional (warning is dev-only, once).
Example fix
// before
<router-link v-slot="{ href, navigate }"><a :href="href" @click="navigate">Go</a></router-link>
// after
<router-link :to="to" custom v-slot="{ href, navigate }"><a :href="href" @click="navigate">Go</a></router-link> Defensive patterns
Strategy: retry
Validate before calling
// preflight: ensure the chunk URL is reachable before navigating
async function chunkReachable (importFn) {
try { await importFn(); return true } catch { return false }
} Type guard
function isChunkError (err) {
return err instanceof Error && /Loading chunk|Loading CSS chunk|ChunkLoadError/i.test(err.message)
} Try / catch
function lazy (importFn, retries = 2) {
return () => importFn().catch(err => {
if (retries-- > 0 && isChunkError(err)) {
window.location.reload()
}
throw err
})
}
{ path: '/admin', component: lazy(() => import('@/views/Admin')) } Prevention
- Keep deploy artifact hashes stable per release; consider caching-busting strategies for long-lived sessions.
- Handle Vue async component error/timeout options where applicable.
- Verify dynamic import paths resolve at build time (bundler warnings).
- Monitor for ChunkLoadError in production error tracking.
When it happens
Trigger: Rendering <router-link v-slot="{ href, navigate }">...</router-link> in dev mode without the custom prop, while also using the default rendering path (i.e. relying on router-link to render the <a> itself).
Common situations: Migrating from vue-router 3 to 4 or using next releases; copying v-slot examples that assume custom; fully custom link components (e.g. wrapping <nuxt-link>-style markup) that forget custom.
Related errors
- <router-link> with to="${this.to}" is trying to use a scoped
- <router-link>'s tag prop is deprecated and has been removed
- router.addRoutes() is deprecated and has been removed in Vue
- In Vue Router 4, the v-slot API will by default wrap its con
- [vue-router]: Missing current instance. ${method}() must be
AI-assisted analysis of vuejs/vue-router@680ccc68c5 (2026-09-02).
Data as JSON: /api/errors/ff8779a77acd3773.
Report an issue: GitHub.