dotnet/efcore · error · InvalidOperationException

The DbFunction '{function}' returns a SqlExpression of type

Error message

The DbFunction '{function}' returns a SqlExpression of type '{type}', which is a nullable value type. DbFunctions must return expressions with non-nullable value types, even when they may return 'null'.

What it means

When a DbFunction registered with a custom HasTranslation is invoked, EF requires the returned SqlExpression to have a non-nullable value Type. This throw fires when translation returns a SqlExpression whose Type.IsNullableValueType() is true (e.g. int?, DateTime?, decimal?). EF's type-mapping/nullability system assumes DbFunction translations expose a non-nullable CLR type; the database-level nullability is expressed separately via the IsNullable flag.

Source

Thrown at src/EFCore.Relational/Query/RelationalMethodCallTranslatorProvider.cs:66

    /// <inheritdoc />
    public virtual SqlExpression? Translate(
        IModel model,
        SqlExpression? instance,
        MethodInfo method,
        IReadOnlyList<SqlExpression> arguments,
        IDiagnosticsLogger<DbLoggerCategory.Query> logger)
    {
        var dbFunction = model.FindDbFunction(method);
        if (dbFunction != null)
        {
            if (dbFunction.Translation != null)
            {
                var translation = dbFunction.Translation.Invoke(
                    arguments.Select(e => _sqlExpressionFactory.ApplyDefaultTypeMapping(e)).ToList());

                return translation.Type.IsNullableValueType()
                    ? throw new InvalidOperationException(
                        RelationalStrings.DbFunctionNullableValueReturnType(
                            dbFunction.ModelName, dbFunction.ReturnType.ShortDisplayName()))
                    : translation;
            }

            var argumentsPropagateNullability = dbFunction.Parameters.Select(p => p.PropagatesNullability);

            return dbFunction.IsBuiltIn
                ? _sqlExpressionFactory.Function(
                    dbFunction.Name,
                    arguments,
                    dbFunction.IsNullable,
                    argumentsPropagateNullability,
                    method.ReturnType.UnwrapNullableType(),
                    dbFunction.TypeMapping)
                : _sqlExpressionFactory.Function(
                    dbFunction.Schema,
                    dbFunction.Name,

View on GitHub (pinned to 3a2006ef56)

Solutions

  1. Return a SqlExpression whose Type is the non-nullable underlying type, e.g. typeof(int) instead of typeof(int?). Use the non-nullable CLR type when constructing SqlFunctionExpression / similar.
  2. If sourcing from a column expression, strip nullability: build a new expression with the non-nullable type mapping rather than reusing one typed as Nullable<T>.
  3. Express database nullability through the IsNullable parameter / type mapping, not through the CLR Type.

Example fix

// before
modelBuilder.HasDbFunction(mi)
    .HasTranslation(args => new SqlFunctionExpression("FOO", args, true, new[] { true }, typeof(int?), null));
// after
modelBuilder.HasDbFunction(mi)
    .HasTranslation(args => new SqlFunctionExpression("FOO", args, true, new[] { true }, typeof(int), null));
Defensive patterns

Strategy: validation

Validate before calling

// Ensure a HasTranslation returns a non-nullable value Type.
static SqlExpression NonNullResult(SqlExpression e, Type nonNullType)
    => e.Type == nonNullType
        ? e
        : throw new InvalidOperationException($"DbFunction translation must return {nonNullType}, got {e.Type}.");

modelBuilder.HasDbFunction(method)
    .HasTranslation(args => NonNullResult(
        /* your SqlExpression */, typeof(int)));

Prevention

When it happens

Trigger: modelBuilder.HasDbFunction(typeof(MyFunctions).GetMethod(nameof(MyFunctions.Foo))).HasTranslation(args => new SqlFunctionExpression("FOO", args, typeof(int?))); — any HasTranslation whose returned SqlExpression.Type is a Nullable<T>. Also when reusing an existing SqlExpression (e.g. a column) that carries int? as its Type.

Common situations: Custom DbFunction translations written to mirror a CLR signature that uses int?/DateTime?; copying a column expression (which often has a nullable Type) into the translation result; upgrading from a version that did not validate the return type.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/25463b907715f95b. Report an issue: GitHub.