dotnet/efcore · error · InvalidOperationException
The parameter ' ' cannot be added to the stored procedure '…
Error message
The parameter '{parameter}' cannot be added to the stored procedure '{sproc}' because another parameter with this name already exists. What it means
Thrown when ValidateStoredProcedure iterates the parameters of a mapped sproc and finds two parameters with the same Name within a single stored procedure mapping. The validator maintains a HashSet<string> parameterNames per sproc and rejects the duplicate; sproc parameters must be uniquely named because EF builds a positional/named argument list keyed by name.
Solutions
- Inspect the sproc mapping and find both calls producing the same parameter Name; give them distinct names via the parameter-name overload: .HasParameter("foo_current", p => p.Foo) and .HasParameter("foo_original", p => p.Foo).
- If one of the duplicates is the row-count/output value, configure it as a rows-affected parameter/result column rather than a regular parameter.
- Remove the redundant HasParameter call entirely if both were intended to map the same property value.
Example fix
// before
entityType.UpdateUsingStoredProcedure("UpdateOrder",
sp =>
{
sp.HasParameter(o => o.RowVersion); // produces "RowVersion"
sp.HasOriginalValueParameter(o => o.RowVersion); // also "RowVersion" -> duplicate
});
// after
entityType.UpdateUsingStoredProcedure("UpdateOrder",
sp =>
{
sp.HasParameter("RowVersion_Current", o => o.RowVersion);
sp.HasOriginalValueParameter("RowVersion_Original", o => o.RowVersion);
}); Defensive patterns
Strategy: validation
Validate before calling
// Assert unique parameter names per sproc mapping.
foreach (var et in model.GetEntityTypes())
{
foreach (var sproc in new[] { et.GetInsertStoredProcedure(), et.GetUpdateStoredProcedure(), et.GetDeleteStoredProcedure() })
{
if (sproc == null) continue;
var names = sproc.Parameters.Select(p => p.Name).ToList();
var dupes = names.GroupBy(n => n).Where(g => g.Count() > 1).Select(g => g.Key);
foreach (var d in dupes)
Console.WriteLine($"Duplicate parameter '{d}' on {sproc.Name}");
}
} Prevention
- When mapping both current and original values of one property, always pass an explicit parameter name.
- Use a small fluent helper to register sproc parameters from a single list, deduplicating by name.
- Run a startup check that calls Database.GetService<IModelValidator>().Validate(model) in tests.
When it happens
Trigger: Two .HasParameter(...) / .HasOriginalValueParameter(...) / .HasResultColumn(...) calls produce the same parameter Name on the same InsertUsingStoredProcedure / UpdateUsingStoredProcedure / DeleteUsingStoredProcedure. Common when HasParameter(p => p.Id) is combined with HasOriginalValueParameter(p => p.Id) without setting distinct names, or when a row-count parameter collides with a property parameter name.
Common situations: Copy-pasting parameter config across insert/update/delete sproc builders; mapping both current value and original value of the same property without specifying separate parameter names; an automatically generated concurrency-token parameter colliding with an explicit one.
Related errors
- A rows affected parameter, result column or return value…
- Both ' ' and ' ' are explicitly mapped to the stored…
- Both ' ' and ' ' are mapped to the stored procedure ' '…
- ' ' is mapped to the stored procedure ' ' while ' ' is…
- Input parameter ' ' of update stored procedure ' ' is…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/11d5528334af90c0.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:782
property.GetAfterSaveBehavior()));
}
break;
case StoreObjectType.DeleteStoredProcedure:
break;
default:
Check.DebugFail("Unexpected stored procedure type: " + storeObjectIdentifier.StoreObjectType);
break;
}
}
}
}
if (!parameterNames.Add(parameter.Name))
{
throw new InvalidOperationException(
RelationalStrings.StoredProcedureDuplicateParameterName(
parameter.Name, storeObjectIdentifier.DisplayName()));
}
if (parameter.PropertyName == null)
{
continue;
}
switch (storeObjectIdentifier.StoreObjectType)
{
case StoreObjectType.InsertStoredProcedure:
case StoreObjectType.UpdateStoredProcedure:
if (parameter.Direction != ParameterDirection.Input
&& !storeGeneratedProperties.Remove(property!.Name))
{
if (sproc.Parameters.Any(p => p.PropertyName == property.Name
&& p.ForOriginalValue != parameter.ForOriginalValueView on GitHub (pinned to 3a2006ef56)