vitest-dev/vitest · error · TypeError

Expected string coverage payload, received

Error message

Expected string coverage payload, received ${typeof coverage}, ${JSON.stringify(coverage)}

What it means

In the blob/coverage flow, onAfterSuiteRun expects the coverage payload to be a string (a path to a serialized coverage artifact written by the worker). If a non-string is supplied (raw object, number, etc.), it throws TypeError. This enforces the worker-to-server coverage contract.

Solutions

  1. Make the coverage provider serialize coverage to a file and return its path string.
  2. Upgrade all @vitest/* packages to matching versions to align the payload contract.
  3. If you implemented a custom provider, write coverage to disk under coverageFilesDirectory and pass the filename.

Example fix

// before - worker returns raw coverage object
return { coverage: coverageMap }

// after - write file, return path string
const file = path.join(dir, `${env}-${Date.now()}.json`)
await fs.writeFile(file, JSON.stringify(coverageMap))
return { coverage: file }
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidCoveragePayload(coverage) {
  return coverage == null || typeof coverage === 'string'
}
// in worker: if (!isValidCoveragePayload(payload.coverage)) write coverage to file and pass path

Type guard

function isCoveragePath(coverage): coverage is string {
  return typeof coverage === 'string' && coverage.length > 0
}

Prevention

When it happens

Trigger: A worker/environment passes a coverage value that is not a string path — e.g. a raw istanbul/v8 coverage object instead of a file path — to onAfterSuiteRun. Common with mismatched coverage provider versions or a custom provider returning the wrong shape.

Common situations: Custom coverage provider returning an object instead of writing a file and returning its path; version skew between @vitest/* packages; middleware transforming the payload.

Related errors


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

Appendix: source

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

      await fs.rm(this.coverageFilesDirectory, {
        recursive: true,
        force: true,
        maxRetries: 10,
      })
    }

    await fs.mkdir(this.coverageFilesDirectory, { recursive: true })

    this.coverageFiles = new Map()
  }

  onAfterSuiteRun({ coverage, environment, projectName, testFiles }: AfterSuiteRunMeta): void {
    if (!coverage) {
      return
    }

    if (typeof coverage !== 'string') {
      throw new TypeError(`Expected string coverage payload, received ${typeof coverage}, ${JSON.stringify(coverage)}`)
    }
    const filename = coverage

    let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT)

    if (!entry) {
      entry = {}
      this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry)
    }

    const testFilenames = testFiles.join()
    entry[environment] ??= {}
    // If there's a result from previous run, overwrite it
    entry[environment][testFilenames] = filename
  }

  async readCoverageFiles<CoverageType>({ onFileRead, onFinished, onDebug }: {
    /** Callback invoked with a single coverage result */

View on GitHub (pinned to 1fa9837ec2)