dotnet/efcore · error · InvalidOperationException
JsonNodeMustBeHandledByProviderSpecificVisitor
JsonNodeMustBeHandledByProviderSpecificVisitor
Error message
This node should be handled by provider-specific SQL generator.
What it means
Thrown by the base relational QuerySqlGenerator.VisitJsonScalar because JsonScalarExpression (a scalar value extracted from a JSON column) can only be rendered using provider-specific JSON functions (e.g. SQL Server JSON_VALUE, PostgreSQL ->/->>). The base relational provider has no JSON syntax, so reaching this visitor means no provider-specific SQL generator handled the node.
Source
Thrown at src/EFCore.Relational/Query/QuerySqlGenerator.cs:1673
for (var i = 1; i < rowValues.Count; i++)
{
if (i > 1)
{
_relationalCommandBuilder.Append(", ");
}
Visit(valuesExpression.RowValues[i]);
}
}
}
/// <summary>
/// Generates SQL for a JSON scalar lookup expression.
/// </summary>
/// <param name="jsonScalarExpression">The <see cref="JsonScalarExpression" /> for which to generate SQL.</param>
protected virtual Expression VisitJsonScalar(JsonScalarExpression jsonScalarExpression)
=> throw new InvalidOperationException(
RelationalStrings.JsonNodeMustBeHandledByProviderSpecificVisitor);
/// <summary>
/// Returns a bool value indicating if the inner SQL expression required to be put inside parenthesis when generating SQL for outer
/// SQL expression.
/// </summary>
/// <param name="outerExpression">The outer expression which provides context in which SQL is being generated.</param>
/// <param name="innerExpression">The inner expression which may need to be put inside parenthesis.</param>
/// <returns>A bool value indicating that parenthesis is required or not. </returns>
protected virtual bool RequiresParentheses(SqlExpression outerExpression, SqlExpression innerExpression)
{
int outerPrecedence, innerPrecedence;
// Convert is rendered as a function (CAST()) and not as an operator, so we never need to add parentheses around the inner
if (outerExpression is SqlUnaryExpression { OperatorType: ExpressionType.Convert })
{
return false;
}View on GitHub (pinned to dbf9771522)
Solutions
- Use a provider that implements JSON support (SQL Server, PostgreSQL with the JSON plugin) so VisitJsonScalar is overridden.
- Remove the JSON mapping (config.ToJson or OwnsOne(...).ToJson()) and map the owned type via table splitting or as a complex type instead.
- Upgrade the provider package to a version with JSON query support.
- If authoring a provider, override VisitJsonScalar (and the JSON visitor hooks) in your QuerySqlGenerator.
Example fix
// before - JSON-owned entity on a provider without JSON SQL support
modelBuilder.Entity<Order>().OwnsOne(o => o.Address, a => a.ToJson("address"));
// after - map via table splitting instead
modelBuilder.Entity<Order>().OwnsOne(o => o.Address);
// or switch to a JSON-capable provider (UseSqlServer / Npgsql with jsonb) Defensive patterns
Strategy: validation
Validate before calling
// Check the provider supports JSON before configuring JSON mapping
var supportsJson = db.Database.ProviderName switch
{
"Microsoft.EntityFrameworkCore.SqlServer" => true,
"Npgsql.EntityFrameworkCore.PostgreSQL" => true,
_ => false,
};
if (!supportsJson) modelBuilder.Entity<Order>().OwnsOne(o => o.Address); // table splitting, no ToJson Try / catch
try { return await db.Orders.Where(o => o.Address.City == "X").ToListAsync(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("provider-specific SQL generator"))
{
// fall back: load entities and filter client-side, or switch mapping
return (await db.Orders.ToListAsync()).Where(o => o.Address?.City == "X").ToList();
} Prevention
- Only enable ToJson on JSON-capable providers (SqlServer, Npgsql).
- Prefer ComplexProperty over ToJson for owned value objects unless JSON storage is required.
- Pin provider versions that match your EF Core runtime version for JSON support.
- If authoring a provider, override VisitJsonScalar and the JSON query hooks.
When it happens
Trigger: Querying JSON-mapped owned/complex properties on a provider that does not implement JSON SQL generation (a bare/incomplete relational provider), or a provider version that supports JSON mapping in the model but not in SQL emission. Triggered by any query that projects or filters a JSON scalar.
Common situations: Using a third-party or in-house relational provider without JSON support; enabling JSON columns in the model (ToJson/owning JSON) against SQLite (pre-JSON support) or another minimal provider; mismatched provider/model where JSON mapping was configured but the SQL generator cannot emit it.
Related errors
- ExecuteOperationOnOwnedJsonIsNotSupported
- Unhandled expression '{expression}' of type '{expressionType
- The store type '{storeType}' specified for JSON column '{col
- The current migration SQL generator '{sqlGeneratorType}' is
- SQL generation for the operation '{operation}' is not suppor
AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06).
Data as JSON: /api/errors/01a7deadcf2f7f89.
Report an issue: GitHub.