vitest-dev/vitest · error · Error

The vcsProvider module

Error message

The vcsProvider module '${vcsProvider}' doesn't have a default export with `findChangedFiles` method.

What it means

Vitest allows a custom VCS provider via the `vcsProvider` config option (a module specifier string). After dynamically importing that module, Vitest requires its `default` export to be an object exposing a `findChangedFiles` function. This error fires when the module loads but its default export is missing, not an object, or lacks the required method — i.e. the module does not conform to the `VCSProvider` interface.

Solutions

  1. Ensure the provider module has `export default { async findChangedFiles(options) { ... } }` returning absolute or root-relative file paths.
  2. Verify the method name is exactly `findChangedFiles` (not `getChangedFiles`/`changedFiles`) and is async returning `Promise<string[]>`.
  3. If you only need git, omit `vcsProvider` or set it to `'git'` to use the built-in GitVCSProvider.
  4. Pass a ready-made object instead of a module string: `vcsProvider: { findChangedFiles: async (opts) => [...] }` to bypass module loading entirely.

Example fix

// before - my-vcs.ts
export function findChangedFiles(options) { return [] }

// after
export default {
  async findChangedFiles(options) {
    // options.root, options.changedSince
    return []
  },
}
Defensive patterns

Strategy: type-guard

Validate before calling

import('./my-vcs.ts').then(m => {
  const ok = m.default != null
    && typeof m.default === 'object'
    && typeof m.default.findChangedFiles === 'function'
  if (!ok) throw new Error('provider missing default.findChangedFiles')
})

Type guard

function isVCSProvider(v: unknown): v is { findChangedFiles(o: { root: string; changedSince?: string | boolean }): Promise<string[]> } {
  return v != null && typeof v === 'object' && typeof (v as any).findChangedFiles === 'function'
}

Try / catch

try { await loadVCSProvider(runner, './my-vcs') } catch (e) { if (/default export/.test(String(e.message))) { /* fix provider module */ } else throw e }

Prevention

When it happens

Trigger: Setting `vcsProvider: './my-vcs'` (or `vcs: { provider: './my-vcs' }`) where the resolved module has no default export, exports a primitive/function as default, or the default object has no `findChangedFiles(options)` method. The check at vcs.ts:23 explicitly tests `!module.default || typeof module.default !== 'object' || typeof module.default.findChangedFiles !== 'function'`.

Common situations: Authoring a custom VCS provider for a non-git VCS (Mercurial, Perforce, SVN) and forgetting the default export; naming the method `getChangedFiles` instead of `findChangedFiles`; exporting a class instance whose prototype carries the method but the static default is a config object; pointing `vcsProvider` at a re-export barrel file that does `export { GitVCSProvider }` instead of `export default new GitVCSProvider()`.

Related errors


AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11). Data as JSON: /api/errors/5a48f2212e6ebcaf. Report an issue: GitHub.

Appendix: source

Thrown at packages/vitest/src/node/vcs/vcs.ts:24

  root: string
  changedSince?: string | boolean
}

export interface VCSProvider {
  // eslint-disable-next-line ts/method-signature-style
  findChangedFiles(options: VCSProviderOptions): Promise<string[]>
}

export async function loadVCSProvider(runner: ModuleRunner, vcsProvider: string | VCSProvider | undefined): Promise<VCSProvider> {
  if (typeof vcsProvider === 'object' && vcsProvider != null) {
    return wrapVCSProvider(vcsProvider)
  }
  if (!vcsProvider || vcsProvider === 'git') {
    return new GitVCSProvider()
  }
  const module = await runner.import(vcsProvider) as { default: VCSProvider }
  if (!module.default || typeof module.default !== 'object' || typeof module.default.findChangedFiles !== 'function') {
    throw new Error(`The vcsProvider module '${vcsProvider}' doesn't have a default export with \`findChangedFiles\` method.`)
  }
  return wrapVCSProvider(module.default)
}

function wrapVCSProvider(provider: VCSProvider): VCSProvider {
  return {
    async findChangedFiles(options) {
      const changedFiles = await provider.findChangedFiles(options)
      return changedFiles.map(file => resolve(options.root, file))
    },
  }
}

View on GitHub (pinned to 1fa9837ec2)