dotnet/efcore · error · InvalidOperationException

ExecuteUpdateCannotSetJsonPropertyToArbitraryExpression

ExecuteUpdateCannotSetJsonPropertyToArbitraryExpression

Error message

'ExecuteUpdate' cannot currently set a property in a JSON column to arbitrary expressions; only constants, parameters and other JSON properties are supported; see https://github.com/dotnet/efcore/issues/36688.

What it means

Same JSON-scalar setter path (line 371-400), but here the value is neither a ColumnExpression nor one of the supported shapes (constant, parameter, or another JsonScalarExpression). TrySerializeScalarToJson returns false for arbitrary expressions, so the else branch at line 391-394 throws ExecuteUpdateCannotSetJsonPropertyToArbitraryExpression (issue #36688). Only constants, parameters, and JSON-to-JSON property copies are supported.

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

  1. Read the current value client-side, compute the new value, and pass it as a constant/parameter.
  2. Use raw SQL with the database's JSON modification function (e.g. JSON_MODIFY) for server-side computation.
  3. Restrict JSON-scalar SetProperty values to literals, captured variables (parameters), or another JSON scalar property copy.

Example fix

// before (arithmetic on a JSON path is not supported)
db.Blogs.ExecuteUpdate(s => s.SetProperty(
    b => b.Json.ViewCount, b => b.Json.ViewCount + 1));

// after (compute client-side, pass as a parameter)
var current = await db.Blogs.Where(b => b.Id == id)
    .Select(b => b.Json.ViewCount).FirstAsync();
db.Blogs.Where(b => b.Id == id)
    .ExecuteUpdate(s => s.SetProperty(
        b => b.Json.ViewCount, current + 1));
Defensive patterns

Strategy: validation

Try / catch

try { await q.ExecuteUpdateAsync(s => s.SetProperty(e => e.Json.Counter, e => e.Json.Counter + 1)); }
catch (InvalidOperationException ex) when (ex.Message.Contains("arbitrary expressions"))
{ /* compute client-side and pass as parameter; or use raw SQL JSON functions */ }

Prevention

When it happens

Trigger: Assigning a computed/arbitrary expression to a JSON scalar: SetProperty(e => e.Json.Counter, e => e.Json.Counter + 1), SetProperty(e => e.Json.Score, e => SomeMethod()), or any function-call/arithmetic expression targeting a JSON path.

Common situations: Trying to increment a JSON counter server-side; calling EF.Functions or custom methods in the value lambda for a JSON property; combining columns/JSON values arithmetically into a JSON target.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/5409cba02b5d80d3. Report an issue: GitHub.