dotnet/efcore · error · InvalidOperationException
ExecuteUpdateCannotSetJsonPropertyToNonJsonColumn
ExecuteUpdateCannotSetJsonPropertyToNonJsonColumn
Error message
'ExecuteUpdate' cannot currently set a property in a JSON column to a regular, non-JSON column; see https://github.com/dotnet/efcore/issues/36688.
What it means
When SetProperty targets a scalar property inside a JSON column (JsonScalarExpression, line 371-400), EF must serialize the value to its JSON representation via TrySerializeScalarToJson. If the value is another regular ColumnExpression (a non-JSON column), there is no way to convert a column's runtime value into JSON server-side, so the non-JSON-column branch at line 391-394 throws ExecuteUpdateCannotSetJsonPropertyToNonJsonColumn (tracked by issue #36688).
Source
Thrown at src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.ExecuteUpdate.cs:391
var typeMapping = jsonScalar.TypeMapping;
Check.DebugAssert(typeMapping is not null);
// We should never see a JsonScalarExpression without a path - that means we're mapping a JSON scalar directly to a relational column.
// This is in theory possible (e.g. map a DateTime to a 'json' column with a single string timestamp representation inside, instead of to
// SQL Server datetime2), but contrived and unsupported.
Check.DebugAssert(jsonScalar.Path.Count > 0);
ProcessColumn(jsonColumn, targetProperty);
var translatedValue = TranslateScalarSetterValueSelector(source, valueSelector, jsonScalar.Type, typeMapping);
// We now have the relational scalar expression for the value; but we need the JSON representation to pass to the provider's JSON modification
// function (e.g. SQL Server JSON_MODIFY()).
// For example, for a DateTime we'd have e.g. a SqlConstantExpression containing a DateTime instance, but we need a string containing
// the JSON-encoded ISO8601 representation.
if (!TrySerializeScalarToJson(jsonScalar, translatedValue, out var jsonValue))
{
throw new InvalidOperationException(
translatedValue is ColumnExpression
? RelationalStrings.ExecuteUpdateCannotSetJsonPropertyToNonJsonColumn
: RelationalStrings.ExecuteUpdateCannotSetJsonPropertyToArbitraryExpression);
}
// We now have a serialized JSON value (number, string or bool) - generate a setter for it.
GenerateJsonPartialUpdateSetterWrapper(jsonScalar, jsonColumn, jsonValue);
continue;
}
case StructuralTypeShaperExpression { ValueBufferExpression: JsonQueryExpression jsonQuery }:
ProcessStructuralJsonSetter(jsonQuery);
continue;
case CollectionResultExpression { QueryExpression: JsonQueryExpression jsonQuery }:
ProcessStructuralJsonSetter(jsonQuery);
continue;
View on GitHub (pinned to dbf9771522)
Solutions
- Assign a constant, a parameter, or another JSON scalar property to the JSON path instead of a regular column.
- Project the source column value into the application first, then pass it as a parameter to ExecuteUpdate.
- Perform the migration via raw SQL using the database's JSON functions if a server-side copy is mandatory.
Example fix
// before (copying a flat column into a JSON path)
db.Records.ExecuteUpdate(s => s.SetProperty(
r => r.JsonData.Notes, r => r.LegacyNotes));
// after (materialize the value client-side and pass as a constant/parameter)
var notes = await db.Records.Where(r => r.Id == id)
.Select(r => r.LegacyNotes).FirstAsync();
db.Records.Where(r => r.Id == id)
.ExecuteUpdate(s => s.SetProperty(
r => r.JsonData.Notes, notes)); Defensive patterns
Strategy: validation
Validate before calling
// Before assigning a column to a JSON scalar path, materialize the source value client-side. // (No runtime API to detect this; guard by reviewing the SetProperty value lambda.) // Rule: JSON scalar SetProperty values must be constant/parameter/JSON-scalar only.
Try / catch
try { await q.ExecuteUpdateAsync(s => s.SetProperty(e => e.Json.X, e => e.PlainCol)); }
catch (InvalidOperationException ex) when (ex.Message.Contains("non-JSON column"))
{ /* read PlainCol client-side and pass as parameter */ } Prevention
- Never assign a relational column directly into a JSON scalar path.
- Materialize source values then pass them as parameters.
- Use raw SQL with JSON_MODIFY-equivalents for server-side column-to-JSON copies.
When it happens
Trigger: Assigning a regular relational column to a JSON scalar path: SetProperty(e => e.JsonDetails.Notes, e => e.PlainTextColumn) where JsonDetails is JSON-mapped and PlainTextColumn is a normal column.
Common situations: Trying to copy a value from one column into a JSON sub-property; modeling data migration via ExecuteUpdate that moves flat columns into JSON; using two entities sharing a table where one side is JSON.
Related errors
- ExecuteUpdateCannotSetJsonPropertyToArbitraryExpression
- ExecuteOperationOnOwnedJsonIsNotSupported
- ExecuteUpdateOverJsonIsNotSupported
- IncompatibleComplexTypesInAssignment
- JsonExecuteUpdateNotSupportedWithOwnedEntities
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/eb1e1dcf010bc2ce.
Report an issue: GitHub.