{"record":{"id":"4470fb6e25980997","repo":"dotnet/efcore","slug":"current-value-parameter-parameter-is-not-allow","errorCode":null,"errorMessage":"Current value parameter '{parameter}' is not allowed on delete stored procedure '{sproc}'. Use HasOriginalValueParameter() instead.","messagePattern":"Current value parameter '(.+?)' is not allowed on delete stored procedure '(.+?)'\\. Use HasOriginalValueParameter\\(\\) instead\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs","lineNumber":732,"sourceCode":"                    if (storeObjectIdentifier.StoreObjectType == StoreObjectType.InsertStoredProcedure)\n                    {\n                        throw new InvalidOperationException(\n                            RelationalStrings.StoredProcedureOriginalValueParameterOnInsert(\n                                parameter.Name, storeObjectIdentifier.DisplayName()));\n                    }\n                }\n                else\n                {\n                    if (!properties.TryGetAndRemove(parameter.PropertyName, out property))\n                    {\n                        throw new InvalidOperationException(\n                            RelationalStrings.StoredProcedureParameterNotFound(\n                                parameter.PropertyName, entityType.DisplayName(), storeObjectIdentifier.DisplayName()));\n                    }\n\n                    if (storeObjectIdentifier.StoreObjectType == StoreObjectType.DeleteStoredProcedure)\n                    {\n                        throw new InvalidOperationException(\n                            RelationalStrings.StoredProcedureCurrentValueParameterOnDelete(\n                                parameter.Name, storeObjectIdentifier.DisplayName()));\n                    }\n\n                    if (parameter.Direction.HasFlag(ParameterDirection.Input))\n                    {\n                        switch (storeObjectIdentifier.StoreObjectType)\n                        {\n                            case StoreObjectType.InsertStoredProcedure:\n                                if (property.GetBeforeSaveBehavior() != PropertySaveBehavior.Save)\n                                {\n                                    throw new InvalidOperationException(\n                                        RelationalStrings.StoredProcedureInputParameterForInsertNonSaveProperty(\n                                            parameter.Name,\n                                            storeObjectIdentifier.DisplayName(),\n                                            parameter.PropertyName,\n                                            entityType.DisplayName(),\n                                            property.GetBeforeSaveBehavior()));","sourceCodeStart":714,"sourceCodeEnd":750,"githubUrl":"https://github.com/dotnet/efcore/blob/3a2006ef569de08368d59db5e1468aa8f407e4f8/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs#L714-L750","documentation":"Thrown on the current-value parameter path when StoreObjectType == DeleteStoredProcedure (RelationalModelValidator.cs:730-735). Delete sprocs operate on the entity's pre-save state, so only original-value parameters are valid; a current-value parameter on delete is rejected. Use HasOriginalValueParameter instead.","triggerScenarios":"Calling .HasParameter(x => x.Foo) (current value) inside a .DeleteUsingStoredProcedure(... ) configuration. Usually copy-paste from insert/update sproc; misunderstanding that delete needs the original key/token, not the current value.","commonSituations":"Refactoring a shared sproc builder across all three CUD operations; pasting an update template into a delete sproc; using a concurrency token via HasParameter instead of HasOriginalValueParameter on delete.","solutions":["Use .HasOriginalValueParameter(x => x.Foo) in the delete sproc configuration.","For the key specifically, ensure it's bound as an original-value parameter (the convention default already does this for the PK — verify you didn't override it).","If you truly need the current value on delete (rare), reconsider the sproc design — EF delete mapping uses pre-save values."],"exampleFix":"// before\nmodelBuilder.Entity<Customer>()\n    .DeleteUsingStoredProcedure(\"dbo.CustomerDelete\", sp =>\n    {\n        sp.HasParameter(c => c.Id); // throws — current value not allowed on delete\n        sp.HasParameter(c => c.VersionTag);\n    });\n\n// after\nmodelBuilder.Entity<Customer>()\n    .DeleteUsingStoredProcedure(\"dbo.CustomerDelete\", sp =>\n    {\n        sp.HasOriginalValueParameter(c => c.Id);\n        sp.HasOriginalValueParameter(c => c.VersionTag);\n    });","handlingStrategy":"validation","validationCode":"foreach (var et in modelBuilder.Model.GetEntityTypes())\n{\n    var delete = et.GetDeleteStoredProcedure();\n    if (delete is null) continue;\n    if (delete.Parameters.Any(p => p.ForOriginalValue != true && p.PropertyName is not null))\n        throw new InvalidOperationException($\"Delete sproc '{delete.GetStoreIdentifier().DisplayName}' on '{et.Name}' must not have current-value parameters; use HasOriginalValueParameter.\");\n}","typeGuard":null,"tryCatchPattern":"try { _ = ctx.Model; }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"not allowed on delete stored procedure\"))\n{\n    logger.LogError(\"Use HasOriginalValueParameter on delete sprocs. {Msg}\", ex.Message);\n    throw;\n}","preventionTips":["Bind only original-value parameters (and unbound value parameters) in DeleteUsingStoredProcedure.","For the PK on delete, rely on the convention default (original value) — don't override it with HasParameter.","Treat delete sproc config as distinct from insert/update."],"tags":["efcore","relational","stored-procedure","parameter","current-value","delete","model-validation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}