dotnet/efcore · error · InvalidOperationException
Input parameter ' ' of update stored procedure ' ' is…
Error message
Input parameter '{parameter}' of update stored procedure '{sproc}' is mapped to property '{property}' of entity type '{entityType}', but that property is configured with AfterSaveBehavior '{behavior}', and so cannot be saved on update. You may need to use HasOriginalValueParameter() instead of HasParameter(). What it means
Thrown during model validation when an UPDATE stored procedure mapping declares an INPUT parameter bound to an entity property whose AfterSaveBehavior is not PropertySaveBehavior.Save. EF Core refuses to send a value the model says is read-only after the entity exists, because the update sproc parameter would imply the property is writable. The validator guards the HasParameter() mapping call against the property's after-save contract.
Solutions
- Remove the offending HasParameter(p => p.Foo) call from the UpdateUsingStoredProcedure configuration since the property is not writable on update.
- If EF should pass the original DB value for concurrency, use HasOriginalValueParameter(p => p.Foo) instead of HasParameter(p => p.Foo).
- If the property genuinely must be written on update, set its after-save behavior to Save: builder.Property(p => p.Foo).Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save) (or remove the .ValueGeneratedOnAdd()/AfterSaveBehavior.Ignore that suppressed it).
- Move that property to an output parameter / result column mapping if the database regenerates it on update.
Example fix
// before
entityType.UpdateUsingStoredProcedure("UpdateCustomer",
sp =>
{
sp.HasParameter(c => c.CreatedOn); // CreatedOn is ValueGeneratedOnAdd
});
// after
entityType.UpdateUsingStoredProcedure("UpdateCustomer",
sp =>
{
// CreatedOn removed; it is not writable on update
sp.HasParameter(c => c.Name);
}); Defensive patterns
Strategy: validation
Validate before calling
// Before finalizing the model, verify each UPDATE sproc input parameter
// maps to a Save-after-save property.
foreach (var et in model.GetEntityTypes())
{
var updateSproc = et.GetUpdateStoredProcedure();
if (updateSproc == null) continue;
foreach (var p in updateSproc.Parameters)
{
if (p.Direction.HasFlag(System.Data.ParameterDirection.Input)
&& p.PropertyName is string name)
{
var prop = et.FindProperty(name);
if (prop?.GetAfterSaveBehavior() != Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Save)
{
Console.WriteLine($"{et.Name}.{name} is not Save on update; remove or use HasOriginalValueParameter.");
}
}
}
} Prevention
- Drive sproc parameter mapping from a single config method per entity so insert/update/delete variants diverge deliberately.
- When you change a property's ValueGenerated / SaveBehavior, search for HasParameter calls that reference it.
- Add a model-validation integration test that builds the model and calls model.FinalizeModel() during unit tests.
When it happens
Trigger: Calling entityType.UpdateUsingStoredProcedure(...).HasParameter(p => p.Foo) where Foo has AfterSaveBehavior != Save (e.g. .ValueGeneratedOnAdd(), .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore), a store-generated computed column, or a property marked read-only after save). Surfaces when the model is finalized (EnsureModelFinalized / first query / SaveChanges) and validator runs.
Common situations: Marking an audit/created-on column or a computed column as input parameter on an update sproc; mapping a property that should only be written on INSERT; switching a column to ValueGeneratedOnAdd but forgetting to drop it from the update sproc parameters; using the same HasParameter call for insert and update sproc configs.
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…
- The entity type ' ' is mapped to the stored procedure ' '…
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/1a9f21f44f43cfa5.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs:758
{
case StoreObjectType.InsertStoredProcedure:
if (property.GetBeforeSaveBehavior() != PropertySaveBehavior.Save)
{
throw new InvalidOperationException(
RelationalStrings.StoredProcedureInputParameterForInsertNonSaveProperty(
parameter.Name,
storeObjectIdentifier.DisplayName(),
parameter.PropertyName,
entityType.DisplayName(),
property.GetBeforeSaveBehavior()));
}
break;
case StoreObjectType.UpdateStoredProcedure:
if (property.GetAfterSaveBehavior() != PropertySaveBehavior.Save)
{
throw new InvalidOperationException(
RelationalStrings.StoredProcedureInputParameterForUpdateNonSaveProperty(
parameter.Name,
storeObjectIdentifier.DisplayName(),
parameter.PropertyName,
entityType.DisplayName(),
property.GetAfterSaveBehavior()));
}
break;
case StoreObjectType.DeleteStoredProcedure:
break;
default:
Check.DebugFail("Unexpected stored procedure type: " + storeObjectIdentifier.StoreObjectType);
break;
}
}View on GitHub (pinned to 3a2006ef56)