dotnet/BenchmarkDotNet · error · Exception
Encountered Duplicate Test ID: '{testCase.DisplayName}' and
Error message
Encountered Duplicate Test ID: '{testCase.DisplayName}' and '{matchingCase}' What it means
VSTestAdapter.GetVsTestCasesFromAssembly throws when two discovered benchmark test cases produce the same TestCase.Id (a Guid). The TestAdapter relies on unique VSTest IDs to map run results back to cases; a collision breaks that mapping. The exception is wrapped and re-logged as 'Failed to load benchmarks from assembly'.
Source
Thrown at src/BenchmarkDotNet.TestAdapter/VSTestAdapter.cs:158
/// <param name="logger">A logger that sends logs to VSTest.</param>
/// <returns>The VSTest test cases inside the given assembly.</returns>
private static List<TestCase> GetVsTestCasesFromAssembly(string assemblyPath, IMessageLogger logger)
{
try
{
// Ensure that the test enumeration is done inside the context of the source directory.
var enumerator = (BenchmarkEnumeratorWrapper)CreateIsolatedType(typeof(BenchmarkEnumeratorWrapper), assemblyPath);
var testCases = enumerator
.GetTestCasesFromAssemblyPathSerialized(assemblyPath)
.Select(SerializationHelpers.Deserialize<TestCase>)
.ToList();
// Validate that all test ids are unique
var idLookup = new Dictionary<Guid, string>();
foreach (var testCase in testCases)
{
if (idLookup.TryGetValue(testCase.Id, out var matchingCase))
throw new Exception($"Encountered Duplicate Test ID: '{testCase.DisplayName}' and '{matchingCase}'");
idLookup[testCase.Id] = testCase.DisplayName;
}
return testCases;
}
catch (Exception ex)
{
logger.SendMessage(TestMessageLevel.Error, $"Failed to load benchmarks from assembly\n{ex}");
throw;
}
}
/// <summary>
/// Runs the benchmarks in the given source.
/// </summary>
/// <param name="source">The dll or exe of the benchmark project.</param>
/// <param name="frameworkHandle">An interface used to communicate with the VSTest host.</param>View on GitHub (pinned to b515068b61)
Solutions
- Rename one of the colliding benchmark methods so display names / IDs differ.
- Ensure [Params]/[ArgumentsSource] sets produce distinct display names.
- Check for accidental duplicate benchmark declarations (e.g. base + derived both exporting the same method).
- Upgrade BenchmarkDotNet.TestAdapter in case the ID-collision is a known fixed bug.
Defensive patterns
Strategy: validation
Try / catch
// The adapter wraps and rethrows; catch at the discovery boundary:
try { var cases = adapter.GetVsTestCasesFromAssembly(path, logger); }
catch (Exception ex) when (ex.Message.Contains("Duplicate Test ID"))
{
log.Error($"Two benchmark cases collided on Id; rename one. {ex.Message}");
} Prevention
- Give benchmark methods distinct names to avoid ID/display-name collisions.
- Keep [Params]/[ArgumentsSource] sets distinct enough to yield unique display names.
- Avoid exporting the same benchmark method from both base and derived types.
- Upgrade BenchmarkDotNet.TestAdapter to pick up ID-generation fixes.
When it happens
Trigger: Two benchmark methods (or method+parameter-set combinations) serialize to TestCase objects whose Guid hashes collide, or two cases were assigned identical display names that feed an unstable ID generator. Encountered during discovery when the VSTest host enumerates the benchmark assembly.
Common situations: Two benchmark methods with identical fully-qualified names in different namespaces that the ID hasher treats identically; [Params] / [ArgumentsSource] combinations that collapse to the same display name; a bug in the ID-generation routine after a rename/refactor; duplicated exported benchmark types via multi-targeting.
Related errors
- 'source' cannot be null or whitespace.
- Missing extension on source '{source}', must have the extens
- Unsupported extension on source '{source}', must have the ex
- Please use Targets property
- Sampling interval change is not supported!
AI-assisted analysis of dotnet/BenchmarkDotNet@b515068b61 (2026-08-13).
Data as JSON: /api/errors/d7206a47e67e8ca8.
Report an issue: GitHub.