vitejs/vite · error · Error

Failed to load PostCSS config

Error message

Failed to load PostCSS config: ${e}

What it means

Thrown when loading the PostCSS config (postcss.config.js / .postcssrc) fails for a reason other than 'no config found'. css.ts:2038 calls `postcssrc`; if the rejection's message does not include 'No PostCSS Config found', the error is rethrown with a prefix. The line-2047 branch covers the rare case where the rejection value is not an Error instance (e.g. a string or object thrown manually).

Solutions

  1. Install missing PostCSS plugins named in the config: `npm install -D tailwindcss autoprefixer postcss-preset-env`.
  2. Open postcss.config.js and fix the syntax/import error; the wrapped message shows the original stack.
  3. Match the config file's module format to your project (`type: module` -> use export default).
  4. Temporarily rename/disable postcss.config.js to confirm it is the source.
  5. Update the offending PostCSS plugin to a Node-compatible version.

Example fix

// before: postcss.config.js
module.exports = { plugins: [require('tailwindcss')] } // tailwindcss not installed

// after
// $ npm install -D tailwindcss
module.exports = { plugins: [require('tailwindcss')] }
Defensive patterns

Strategy: try-catch

Validate before calling

import { postcssrc } from 'postcss-load-config'
async function validatePostcssConfig(root: string) {
  try {
    await postcssrc({}, root)
  } catch (e: any) {
    if (!String(e?.message ?? '').includes('No PostCSS Config found')) {
      throw new Error(`PostCSS config is broken: ${e.message}`)
    }
  }
}
// await validatePostcssConfig(process.cwd()) before build

Try / catch

try {
  await build()
} catch (e) {
  if (e instanceof Error && /Failed to load PostCSS config/.test(e.message)) {
    console.error('PostCSS config error. Open postcss.config.js; install named plugins; fix syntax.')
    // optionally: rename postcss.config.js to skip and retry
  }
  throw e
}

Prevention

When it happens

Trigger: A syntax error in postcss.config.js, an import/require failure inside it (e.g. referencing an uninstalled PostCSS plugin like tailwindcss/autoprefixer), or a thrown non-Error value. `postcssrc(...).catch` wraps Errors with name/message/stack; non-Error rejections hit line 2047.

Common situations: Referencing `require('tailwindcss')` in postcss.config.js without installing it. Syntax error in the config after editing. Using ESM/CJS mismatch (config is `.mjs` but uses `module.exports`). A plugin throws a plain string instead of an Error. Node version incompatibility with a PostCSS plugin.

Related errors


AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11). Data as JSON: /api/errors/d27392b13c600a17. Report an issue: GitHub.

Appendix: source

Thrown at packages/vite/src/node/plugins/css.ts:2047

    delete options.plugins
    result = {
      options,
      plugins: inlineOptions.plugins || [],
    }
  } else {
    const searchPath =
      typeof inlineOptions === 'string' ? inlineOptions : config.root
    const stopDir = searchForWorkspaceRoot(config.root)
    result = postcssrc({}, searchPath, { stopDir }).catch((e) => {
      if (!e.message.includes('No PostCSS Config found')) {
        if (e instanceof Error) {
          const { name, message, stack } = e
          e.name = 'Failed to load PostCSS config'
          e.message = `Failed to load PostCSS config (searchPath: ${searchPath}): [${name}] ${message}\n${stack}`
          e.stack = '' // add stack to message to retain stack
          throw e
        } else {
          throw new Error(`Failed to load PostCSS config: ${e}`)
        }
      }
      return null
    })
    // replace cached promise to result object when finished
    result.then(
      (resolved) => {
        postcssConfigCache.set(config, resolved)
      },
      () => {
        /* keep as rejected promise, will be handled later */
      },
    )
  }

  postcssConfigCache.set(config, result)
  return result
}

View on GitHub (pinned to b4d66fee14)