vitest-dev/vitest · error · TypeError

invalid return value in globalSetup file ${globalSetupFile.f

Error message

invalid return value in globalSetup file ${globalSetupFile.file}. Must return a function

What it means

A globalSetup file may return a teardown function that Vitest calls after the whole run. In project.ts:226-237 Vitest calls setup(this); if the returned value is not null/undefined and the module did not already export a named teardown, then the returned value MUST be a function. Returning any other type (object, string, number, array) is rejected because Vitest cannot invoke it as a teardown.

Source

Thrown at packages/vitest/src/node/project.ts:232

  /** @internal */
  async _initializeGlobalSetup() {
    if (this._globalSetups) {
      return
    }

    this._globalSetups = await loadGlobalSetupFiles(
      this.runner,
      this.config.globalSetup,
    )

    for (const globalSetupFile of this._globalSetups) {
      const teardown = await globalSetupFile.setup?.(this)
      if (teardown == null || !!globalSetupFile.teardown) {
        continue
      }
      if (typeof teardown !== 'function') {
        throw new TypeError(
          `invalid return value in globalSetup file ${globalSetupFile.file}. Must return a function`,
        )
      }
      globalSetupFile.teardown = teardown
    }
  }

  onTestsRerun(cb: OnTestsRerunHandler): void {
    this.vitest.onTestsRerun(cb)
  }

  /** @internal */
  async _teardownGlobalSetup(): Promise<void> {
    if (!this._globalSetups) {
      return
    }
    for (const globalSetupFile of [...this._globalSetups].reverse()) {
      await globalSetupFile.teardown?.()

View on GitHub (pinned to d568f8ce37)

Solutions

  1. Return either undefined/nothing or a single function from setup: return async () => { await db.close() }.
  2. If you need to return structured data, export a named teardown function from the same module instead of returning from setup.
  3. Make sure an async setup's last statement / return value is the teardown function, not a data object.

Example fix

// before
export default function setup(project) {
  const client = createClient()
  return { client } // object, not a function
}

// after
export default function setup(project) {
  const client = createClient()
  return async () => { await client.close() }
}
Defensive patterns

Strategy: validation

Validate before calling

const teardown = await setupModule.setup?.(project)
if (teardown != null && typeof teardown !== 'function' && !setupModule.teardown) {
  throw new TypeError(`globalSetup '${file}' must return a function, got ${typeof teardown}`)
}

Type guard

function isTeardownFn(v: unknown): v is (() => unknown) | undefined | null {
  return v == null || typeof v === 'function'
}

Prevention

When it happens

Trigger: A globalSetup file whose default/named setup returns an object (e.g. an API client or a state holder), a string, a number, or a Promise resolving to a non-function, while also not exporting a teardown function.

Common situations: Returning a cleanup descriptor object like { cleanup: () => {} } instead of the function directly; returning a DB handle you intend to close later; forgetting that an async setup returns the value of the last awaited expression which isn't a function.

Related errors


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