{"id":"2bb8a6f3802fb704","repo":"vitest-dev/vitest","slug":"bench-compare-requires-at-least-2-benchmarks","errorCode":null,"errorMessage":"`bench.compare()` requires at least 2 benchmarks, received ${registrations.length} instead. ${registrations.length === 1 ? 'Consider calling `bench().run()`. ' : 'Define benchmarks by calling `bench()`. '}See https://vitest.dev/guide/benchmarking#comparing-benchmarks","messagePattern":"`bench\\.compare\\(\\)` requires at least 2 benchmarks, received (.+?) instead\\. (.+?)See https://vitest\\.dev/guide/benchmarking#comparing-benchmarks","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"packages/vitest/src/runtime/benchmark.ts","lineNumber":475,"sourceCode":"    validateBenchmarkProject(config)\n\n    // extract optional trailing BenchRunOptions argument\n    const lastArg = args.at(-1)\n    const isOptions = lastArg != null && typeof lastArg === 'object' && !(kRegistration in lastArg)\n    const benchOptions = isOptions ? args.pop() as BenchRunOptions : undefined\n    const registrations = args as BenchRegistration<any>[]\n\n    // Mark every passed-in registration as consumed before validation so a\n    // throwing `bench.compare()` (wrong arity, wrong shape) doesn't also\n    // trigger the unrun-bench warning — the user's intent was to consume them.\n    for (const reg of registrations) {\n      if (reg != null && typeof reg === 'object' && kRegistration in reg) {\n        pending.delete(reg)\n      }\n    }\n\n    if (registrations.length < 2) {\n      throw new SyntaxError(`\\`bench.compare()\\` requires at least 2 benchmarks, received ${registrations.length} instead. ${registrations.length === 1 ? 'Consider calling `bench().run()`. ' : 'Define benchmarks by calling `bench()`. '}See https://vitest.dev/guide/benchmarking#comparing-benchmarks`)\n    }\n    for (const reg of registrations) {\n      if (reg == null || typeof reg !== 'object' || !(kRegistration in reg)) {\n        throw new SyntaxError('`bench.compare()` expects every argument to be the return value of `bench` or `bench.from`.')\n      }\n    }\n\n    const runnable: RunnableRegistration<any>[] = []\n    const fromEntries: FromRegistration<any>[] = []\n    for (const reg of registrations) {\n      if (isFromRegistration(reg)) {\n        fromEntries.push(reg)\n      }\n      else {\n        runnable.push(reg as RunnableRegistration<any>)\n      }\n    }\n","sourceCodeStart":457,"sourceCodeEnd":493,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/vitest/src/runtime/benchmark.ts#L457-L493","documentation":"`bench.compare()` runs several registrations together and emits a comparison table, so it needs at least two. This `SyntaxError` (see `benchmark.ts:475`) is thrown when fewer than two registrations are passed; the message adapts: with one registration it suggests `.run()`, with zero it tells you to define benchmarks via `bench()`. Registrations are marked consumed before validation so a thrown compare doesn't also trip the unrun-bench warning.","triggerScenarios":"`await bench.compare()` with no args; `await bench.compare(singleReg)`; building a registrations array dynamically that ends up empty or singleton.","commonSituations":"Conditionally pushing registrations into an array that's sometimes empty; refactoring a single benchmark and forgetting to switch from `compare` to `.run()`; passing a single `bench.from` baseline.","solutions":["If you have exactly one benchmark, call `await reg.run()` instead of `bench.compare(reg)`.","If comparing, pass at least two registrations: `await bench.compare(regA, regB)`.","Guard dynamic arrays: only call `compare` when `regs.length >= 2`, otherwise call `.run()` on the single registration."],"exampleFix":"// before\nconst regs = []\nif (cond) regs.push(bench('a', fnA))\nawait bench.compare(...regs)\n\n// after\nif (regs.length >= 2) await bench.compare(...regs)\nelse if (regs.length === 1) await regs[0].run()","handlingStrategy":"validation","validationCode":"// Only call compare when you have 2+ registrations\nconst regs = [maybeA, maybeB].filter(Boolean) as BenchRegistration<string>[]\nlet storage\nif (regs.length >= 2) {\n  storage = await bench.compare(...regs)\n} else if (regs.length === 1) {\n  storage = { get: () => await regs[0].run() }\n} else {\n  throw new Error('No benchmarks defined to compare or run')\n}","typeGuard":null,"tryCatchPattern":"try {\n  await bench.compare(...regs)\n} catch (e) {\n  if (e instanceof SyntaxError && /requires at least 2 benchmarks/.test(e.message)) {\n    // fall back to running the single registration\n    if (regs.length === 1) await regs[0].run()\n  } else throw e\n}","preventionTips":["Guard dynamic registration arrays with a length check before calling compare.","For a single benchmark, prefer reg.run() over bench.compare(reg).","Log the registration count during development to catch empty arrays early."],"tags":["benchmark","bench-compare","validation","arity","syntaxerror"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}