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
- Make the coverage provider serialize coverage to a file and return its path string.
- Upgrade all @vitest/* packages to matching versions to align the payload contract.
- 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
- Keep all @vitest/* packages at the same version to preserve the payload contract.
- Custom coverage providers must serialize to a file and return the path string.
- Unit-test the worker-to-server coverage payload shape in isolation.
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
- Cannot parse ' ' because "module.stripTypeScriptTypes" is…
- Custom CoverageProviderModule loaded from
- ${error instanceof Error ? error.message : String(error)}
- Expected config.test.coverage.thresholds to be an object
- Failed to load . Default export is missing.
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)