vitest-dev/vitest · error · Error
invalid export in globalSetup file ${file}: ${exp} must be a
Error message
invalid export in globalSetup file ${file}: ${exp} must be a function What it means
loadGlobalSetupFile (globalSetup.ts:28) imports the module and, for each of default/setup/teardown, requires that a defined export be a function. If any of those names is exported as something else (object, string, array), vitest rejects it rather than calling a non-function.
Source
Thrown at packages/vitest/src/node/globalSetup.ts:28
export async function loadGlobalSetupFiles(
runner: ModuleRunner,
globalSetup: string | string[],
): Promise<GlobalSetupFile[]> {
const globalSetupFiles = toArray(globalSetup)
return Promise.all(
globalSetupFiles.map(file => loadGlobalSetupFile(file, runner)),
)
}
async function loadGlobalSetupFile(
file: string,
runner: ModuleRunner,
): Promise<GlobalSetupFile> {
const m = await runner.import(file)
for (const exp of ['default', 'setup', 'teardown']) {
if (m[exp] != null && typeof m[exp] !== 'function') {
throw new Error(
`invalid export in globalSetup file ${file}: ${exp} must be a function`,
)
}
}
if (m.default) {
return {
file,
setup: m.default,
}
}
else if (m.setup || m.teardown) {
return {
file,
setup: m.setup,
teardown: m.teardown,
}
}
else {View on GitHub (pinned to d568f8ce37)
Solutions
- Make each of default/setup/teardown a function (or remove it).
- Rename unrelated exports so they don't collide with the reserved names.
- Use the default-export form: export default function setup(provide) { ... }.
Example fix
// before
export const setup = { hooks: [...] }
// after
export function setup(provide) { /* ... */ } Defensive patterns
Strategy: type-guard
Validate before calling
for (const k of ['default', 'setup', 'teardown']) {
if (mod[k] != null && typeof mod[k] !== 'function') {
throw new TypeError(`globalSetup export '${k}' must be a function`)
}
} Type guard
function isFunctionExport(mod: any, key: string): boolean {
return mod[key] == null || typeof mod[key] === 'function'
} Prevention
- Reserve default/setup/teardown names for functions only.
- Rename helper exports to avoid collisions.
- Lint globalSetup files for the reserved export shapes.
When it happens
Trigger: A globalSetup file that exports default, setup, or teardown as a non-function value — e.g. a config object, an array of hooks, or a constant.
Common situations: Accidentally exporting a config/helper object under one of the reserved names; refactoring a setup file and leaving a stale export.
Related errors
- invalid globalSetup file ${file}. Must export setup, teardow
- Environment "${name}" is not a valid environment. Path "${pa
- Tag "${tag.name}": priority must be a non-negative number.
- Unknown value for "test.listTags": ${listTags}
- Expected config.test.coverage.thresholds to be an object
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/2d50f13e90d7f6ee.json.
Report an issue: GitHub.