diff --git a/packages/@vuepress/core/lib/node/loadTheme.js b/packages/@vuepress/core/lib/node/loadTheme.js
index 3cba5c0dbe..2ad934e276 100755
--- a/packages/@vuepress/core/lib/node/loadTheme.js
+++ b/packages/@vuepress/core/lib/node/loadTheme.js
@@ -93,11 +93,10 @@ function resolveTheme (ctx, resolver, ignoreLocal, theme) {
     path = localThemePath
     name = shortcut = 'local'
     logger.tip(`Apply local theme at ${chalk.gray(path)}...`)
-
-    // 2. From dep
   } else if (isString(theme)) {
+    // 2. From dep
     const resolved = resolver.resolve(theme, sourceDir)
-    if (resolved.entry === null) {
+    if (!resolved.entry) {
       throw new Error(`Cannot resolve theme: ${theme}.`)
     }
     path = normalizeThemePath(resolved)
@@ -108,7 +107,7 @@ function resolveTheme (ctx, resolver, ignoreLocal, theme) {
   }
 
   try {
-    entry = pluginAPI.normalizePlugin(path, ctx.themeConfig)
+    entry = pluginAPI.normalizePlugin('theme', path, ctx.themeConfig)
   } catch (error) {
     entry = {}
   }
diff --git a/packages/@vuepress/core/lib/node/plugin-api/index.js b/packages/@vuepress/core/lib/node/plugin-api/index.js
index 5a9da13a36..b2b31d6bfc 100644
--- a/packages/@vuepress/core/lib/node/plugin-api/index.js
+++ b/packages/@vuepress/core/lib/node/plugin-api/index.js
@@ -85,7 +85,7 @@ module.exports = class PluginAPI {
       plugin = pluginRaw
     } else {
       try {
-        plugin = this.normalizePlugin(pluginRaw, pluginOptions)
+        plugin = this.normalizePlugin('plugin', pluginRaw, pluginOptions)
       } catch (e) {
         logger.warn(e.message)
         return this
@@ -116,10 +116,14 @@ module.exports = class PluginAPI {
    * @api public
    */
 
-  normalizePlugin (pluginRaw, pluginOptions = {}) {
+  normalizePlugin (type, pluginRaw, pluginOptions = {}) {
     let plugin = this._pluginResolver.resolve(pluginRaw)
     if (!plugin.entry) {
-      throw new Error(`[vuepress] cannot resolve plugin "${pluginRaw}"`)
+      if (plugin.error) {
+        throw new Error(`[vuepress] An error was encounted in ${type} "${pluginRaw}"`)
+      } else {
+        throw new Error(`[vuepress] Cannot resolve ${type} "${pluginRaw}"`)
+      }
     }
     plugin = flattenPlugin(plugin, pluginOptions, this._pluginContext, this)
     plugin.$$normalized = true
diff --git a/packages/@vuepress/shared-utils/src/moduleResolver.ts b/packages/@vuepress/shared-utils/src/moduleResolver.ts
index ecab531b3f..c41e8b334d 100644
--- a/packages/@vuepress/shared-utils/src/moduleResolver.ts
+++ b/packages/@vuepress/shared-utils/src/moduleResolver.ts
@@ -24,22 +24,17 @@ const SCOPE_PACKAGE_RE = /^@(.*)\/(.*)/
  */
 
 export class CommonModule {
-  name: string | null
-  entry: string | null
-  shortcut: string | null
-  fromDep: boolean | null
-
   constructor (
-    entry: string | null,
-    name: string | null,
-    shortcut: string | null,
-    fromDep: boolean | null,
-  ) {
-    this.entry = entry
-    this.shortcut = shortcut
-    this.name = name
-    this.fromDep = fromDep
-  }
+    public entry: string | null,
+    public name: string | null,
+    public shortcut: string | null,
+    public fromDep: boolean | null,
+    public error?: Error
+  ) {}
+}
+
+function getNoopModule(error?: Error) {
+  return new CommonModule(null, null, null, null, error)
 }
 
 export interface NormalizedModuleRequest {
@@ -99,17 +94,15 @@ class ModuleResolver {
     }
 
     const isStringRequest = isString(req)
-    const isAbsolutePath = isStringRequest && path.isAbsolute(req)
 
     const resolved = tryChain<string, CommonModule>([
       [this.resolveNonStringPackage.bind(this), !isStringRequest],
-      [this.resolveAbsolutePathPackage.bind(this), isStringRequest && isAbsolutePath],
-      [this.resolveRelativePathPackage.bind(this), isStringRequest && !isAbsolutePath],
+      [this.resolvePathPackage.bind(this), isStringRequest],
       [this.resolveDepPackage.bind(this), isStringRequest]
     ], req)
 
     if (!resolved) {
-      return new CommonModule(null, null, null, null /* fromDep */)
+      return getNoopModule()
     }
 
     return resolved
@@ -128,16 +121,20 @@ class ModuleResolver {
    * Resolve non-string package, return directly.
    */
 
-  private resolveNonStringPackage (req: string) {
-    const { shortcut, name } = <NormalizedModuleRequest>this.normalizeRequest(req)
+  private resolveNonStringPackage (req: any) {
+    const { shortcut, name } = this.normalizeRequest(req)
     return new CommonModule(req, name, shortcut, false /* fromDep */)
   }
 
   /**
-   * Resolve module with absolute path.
+   * Resolve module with absolute/relative path.
    */
 
-  resolveAbsolutePathPackage (req: string) {
+  resolvePathPackage (req: string) {
+    if (!path.isAbsolute(req)) {
+      req = path.resolve(this.cwd, req)
+    }
+
     const normalized = fsExistsFallback([
       req,
       req + '.js',
@@ -149,18 +146,13 @@ class ModuleResolver {
     }
 
     const dirname = path.parse(normalized).name
-    const { shortcut, name } = this.normalizeRequest(dirname)
-    const module = this.load ? require(normalized) : normalized
-    return new CommonModule(module, name, shortcut, false /* fromDep */)
-  }
-
-  /**
-   * Resolve module with absolute path.
-   */
-
-  private resolveRelativePathPackage (req: string) {
-    req = path.resolve(process.cwd(), req)
-    return this.resolveAbsolutePathPackage(req)
+    const { shortcut, name } = this.normalizeName(dirname)
+    try {
+      const module = this.load ? require(normalized) : normalized
+      return new CommonModule(module, name, shortcut, false /* fromDep */)
+    } catch (error) {
+      return getNoopModule(error)
+    }
   }
 
   /**
@@ -168,11 +160,15 @@ class ModuleResolver {
    */
 
   private resolveDepPackage (req: string) {
-    const { shortcut, name } = this.normalizeRequest(req)
-    const entry = this.load
-      ? loadModule(<string>name, this.cwd)
-      : resolveModule(<string>name, this.cwd)
-    return new CommonModule(entry, name, shortcut, true /* fromDep */)
+    const { shortcut, name } = this.normalizeName(req)
+    try {
+      const entry = this.load
+        ? loadModule(<string>name, this.cwd)
+        : resolveModule(<string>name, this.cwd)
+      return new CommonModule(entry, name, shortcut, true /* fromDep */)
+    } catch (error) {
+      return getNoopModule(error)
+    }
   }
 
   /**
@@ -190,8 +186,8 @@ class ModuleResolver {
    */
 
   normalizeName (req: string): NormalizedModuleRequest {
-    let name
-    let shortcut
+    let name = null
+    let shortcut = null
 
     if (req.startsWith('@')) {
       const pkg = resolveScopePackage(req)
@@ -213,7 +209,6 @@ class ModuleResolver {
       name = `${this.nonScopePrefix}${shortcut}`
     }
 
-    // @ts-ignore
     return { name, shortcut }
   }