vitest-dev/vitest · error · Error
Benchmark artifact path
Error message
Benchmark artifact path "${relativePath}" resolves outside the project root (${root}). Paths passed to `writeResult` and `bench.from()` must point inside the project. What it means
A path-traversal guard inside `BenchmarkManager.resolve()`. Before reading or writing a benchmark baseline artifact via `bench.from()`/`writeResult`, the manager resolves the user-supplied path against the project `root` and rejects any result that does not equal or sit under `root/`. This prevents a benchmark file from reading or clobbering files outside the workspace.
Solutions
- Keep baseline artifacts inside the project tree, e.g. `bench.from('./.bench/baseline.json')`.
- If you need a shared baseline location, set Vitest `root` (or `bench` output dir) to that location so the path resolves inside it.
- Replace backslashes and `..` segments; verify with `path.resolve(root, relative).startsWith(root + path.sep)` before calling.
- For symlinks, resolve real paths and ensure the target is within root before passing to `bench.from()`.
Example fix
// before — escapes project root
bench.from('/shared/baselines/core.json')
// after — keep inside the project
bench.from('./baselines/core.json')
// or set root to the shared dir when starting Vitest: vitest --root /shared/baselines Defensive patterns
Strategy: validation
Validate before calling
import { isAbsolute, resolve, relative } from 'node:path'
function assertWithinRoot(root: string, p: string): string {
const abs = isAbsolute(p) ? resolve(p) : resolve(root, p)
const rel = relative(root, abs)
if (rel.startsWith('..') || isAbsolute(rel)) {
throw new Error(`Refusing to read path outside root: ${p}`)
}
return abs
}
const safe = assertWithinRoot(project.config.root, baselinePath)
bench.from(safe) Type guard
function isWithinRoot(root: string, p: string): boolean {
const abs = resolve(root, p)
const rootWithSep = root.endsWith('/') ? root : root + '/'
return abs === root || abs.startsWith(rootWithSep)
} Prevention
- Never accept absolute paths from untrusted input for benchmark baselines.
- Keep baseline files under a dedicated in-project directory like `./.bench/`.
- Sanitize `..` segments before passing paths to `bench.from()`.
When it happens
Trigger: Calling `bench.from('../../../etc/passwd')`, `bench.from('/tmp/baseline.json')` (absolute path outside root), `bench.from('..\..\secrets\key')` on Windows, or passing a symlink-laden path that `pathe/resolve` normalizes outside the project root. Affects both `readResult` and `writeResult` paths.
Common situations: Monorepo where the benchmark file is in `packages/a/` but `root` was resolved to a parent workspace and the relative path escapes; CI where `root` is `/repo` but baseline stored in a shared `/baselines` volume; accidentally passing an absolute cache path to `bench.from()`.
Related errors
- `bench.compare()` expects every argument to be the return…
- `bench.compare()` requires at least 2 benchmarks, received
- `bench()` expects a benchmark function. Call `bench(name…
- `bench.from()` expects a string path or a function…
- `bench.from()` requires a name (string or named function)…
AI-assisted analysis of vitest-dev/vitest@1fa9837ec2 (2026-08-11).
Data as JSON: /api/errors/ee6f9da4f6595794.
Report an issue: GitHub.
Appendix: source
Thrown at packages/vitest/src/node/benchmark.ts:21
import { existsSync } from 'node:fs'
import { mkdir, readFile, writeFile } from 'node:fs/promises'
import { dirname, isAbsolute, resolve } from 'pathe'
export class BenchmarkManager {
constructor(private project: TestProject) {}
// Resolve a user-supplied path against the project root. Reject paths that
// escape the project root: `bench.from()` accepts arbitrary input, and we
// never want a benchmark file to be able to read or clobber files outside
// the workspace.
public resolve(relativePath: string): string {
const root = this.project.config.root
const absolute = isAbsolute(relativePath)
? resolve(relativePath)
: resolve(root, relativePath)
const rootWithSep = root.endsWith('/') ? root : `${root}/`
if (absolute !== root && !absolute.startsWith(rootWithSep)) {
throw new Error(
`Benchmark artifact path "${relativePath}" resolves outside the project root (${root}). `
+ `Paths passed to \`writeResult\` and \`bench.from()\` must point inside the project.`,
)
}
return absolute
}
async readResult(relativePath: string): Promise<BaselineData | null> {
const path = this.resolve(relativePath)
if (!existsSync(path)) {
return null
}
return JSON.parse(await readFile(path, 'utf-8')) as BaselineData
}
async writeResult(relativePath: string, data: BaselineData): Promise<void> {
const absolute = this.resolve(relativePath)
await mkdir(dirname(absolute), { recursive: true })View on GitHub (pinned to 1fa9837ec2)