vitest-dev/vitest · error · Error

Failed to update coverage thresholds. Configuration file is

Error message

Failed to update coverage thresholds. Configuration file is too complex.

What it means

resolveConfig (coverage.ts:903) final fallback: vitest tried inline object, defineConfig(...), and mergeConfig(...) shapes and none matched, so it cannot safely rewrite thresholds. The config is too complex for static rewriting.

Source

Thrown at packages/vitest/src/node/coverage.ts:903

    let config = resolveDefineConfig(mod)
    if (config) {
      return config
    }

    // "export default mergeConfig(..., defineConfig(...))"
    if (mod.$type === 'function-call' && mod.$callee === 'mergeConfig') {
      config = resolveMergeConfig(mod)
      if (config) {
        return config
      }
    }
  }
  catch (error) {
    // Reduce magicast's verbose errors to readable ones
    throw new Error(error instanceof Error ? error.message : String(error))
  }

  throw new Error(
    'Failed to update coverage thresholds. Configuration file is too complex.',
  )
}

function resolveDefineConfig(mod: any) {
  if (mod.$type === 'function-call' && mod.$callee === 'defineConfig') {
    // "export default defineConfig({ test: {...} })"
    if (mod.$args[0].$type === 'object') {
      return mod.$args[0]
    }

    if (mod.$args[0].$type === 'arrow-function-expression') {
      if (mod.$args[0].$body.$type === 'object') {
        // "export default defineConfig(() => ({ test: {...} }))"
        return mod.$args[0].$body
      }

      // "export default defineConfig(() => mergeConfig({...}, ...))"

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Use the standard form: export default defineConfig({ test: { coverage: { thresholds: {...} } } }).
  2. Disable coverage.thresholds.autoUpdate and update the numbers manually.
  3. Inline any wrapper so the thresholds object is statically visible to magicast.

Example fix

// before
export default wrapConfig(defineConfig({ test: { coverage: { thresholds: {...} } } }))

// after
export default defineConfig({ test: { coverage: { thresholds: { lines: 80 } } } })
Defensive patterns

Strategy: validation

Validate before calling

const mod = await parseModule(await fs.readFile(cfgPath, 'utf-8'))
const def = mod.exports.default
const ok = def?.$type === 'object'
  || def?.$callee === 'defineConfig'
  || def?.$callee === 'mergeConfig'
if (!ok) throw new Error('Config shape not supported for autoUpdate')

Type guard

function isRecognizedConfigShape(def: any): boolean {
  return def?.$type === 'object'
    || (def?.$type === 'function-call' && ['defineConfig', 'mergeConfig'].includes(def.$callee))
}

Prevention

When it happens

Trigger: Config uses a pattern vitest doesn't recognize — custom wrapper around defineConfig, factory functions, re-exports, or third-party compose helpers — so the AST walker can't find the thresholds node.

Common situations: Shared monorepo config helpers, configs that call defineConfig through indirection, configs built by plugins.

Related errors


AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03). Data as JSON: /data/errors/bf8ba6a46952004a.json. Report an issue: GitHub.