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

  1. Rename one of the colliding benchmark methods so display names / IDs differ.
  2. Ensure [Params]/[ArgumentsSource] sets produce distinct display names.
  3. Check for accidental duplicate benchmark declarations (e.g. base + derived both exporting the same method).
  4. 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

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


AI-assisted analysis of dotnet/BenchmarkDotNet@b515068b61 (2026-08-13). Data as JSON: /api/errors/d7206a47e67e8ca8. Report an issue: GitHub.