dotnet/efcore · error · InvalidOperationException
Custom translation cannot be set on the DbFunction
Error message
Custom translation cannot be set on the DbFunction '{function}' since it is not a scalar function. What it means
Custom translation (a Func<IReadOnlyList<SqlExpression>,SqlExpression>) only makes sense for scalar functions where EF translates the call into a single SQL expression. SetTranslation throws when translation is non-null and the function is not scalar or is an aggregate, because there is no single SQL expression to produce for a row set.
Solutions
- Remove the HasTranslation / SetTranslation call from the TVF DbFunction — TVFs are mapped by name/schema, not by expression translation.
- If you genuinely need custom SQL expression translation, convert the function back to a scalar return type so it qualifies for a translator.
Example fix
// before
public IQueryable<Order> Search(string term) => throw new NotSupportedException();
modelBuilder.HasDbFunction(typeof(AppDb).GetMethod(nameof(Search)))
.HasTranslation(args => new SqlFragmentExpression("..."));
// after
modelBuilder.HasDbFunction(typeof(AppDb).GetMethod(nameof(Search)));
// TVF mapping is by schema/name; no HasTranslation Defensive patterns
Strategy: validation
Validate before calling
var fn = modelBuilder.HasDbFunction(typeof(AppDb).GetMethod(nameof(AppDb.Search))!).Metadata;
if (fn.IsScalar && !fn.IsAggregate)
{
// safe to call HasTranslation
}
else
{
// skip HasTranslation for TVF / aggregate
} Type guard
static bool SupportsCustomTranslation(IReadOnlyDbFunction fn) => fn.IsScalar && !fn.IsAggregate;
Prevention
- Reserve HasTranslation for scalar DbFunctions only.
- Document which functions are TVFs in your context so translators are not mistakenly attached.
When it happens
Trigger: Calling HasTranslation / SetTranslation on a DbFunction that returns IQueryable<T> (a TVF) or is marked as an aggregate function.
Common situations: Copy-pasting scalar DbFunction configuration (which includes HasTranslation) onto a TVF; refactor that converted a scalar function into a TVF without dropping the custom translator.
Related errors
- 'IsNullable' cannot be set on DbFunction
- 'PropagatesNullability' cannot be set on parameter
- The DbFunction ' ' has an invalid return type ' '…
- The DbFunction ' ' defined on type ' ' must be either a…
- The DbFunction ' ' does not have a parameter named ' '.
AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11).
Data as JSON: /api/errors/512631e12a7cb1e4.
Report an issue: GitHub.
Appendix: source
Thrown at src/EFCore.Relational/Metadata/Internal/DbFunction.cs:611
set => SetTranslation(value, ConfigurationSource.Explicit);
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual Func<IReadOnlyList<SqlExpression>, SqlExpression>? SetTranslation(
Func<IReadOnlyList<SqlExpression>, SqlExpression>? translation,
ConfigurationSource configurationSource)
{
EnsureMutable();
if (translation != null
&& (!IsScalar || IsAggregate))
{
throw new InvalidOperationException(RelationalStrings.DbFunctionNonScalarCustomTranslation(MethodInfo?.DisplayName()));
}
_translation = translation;
_translationConfigurationSource = translation == null
? null
: configurationSource.Max(_translationConfigurationSource);
return translation;
}
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual ConfigurationSource? GetTranslationConfigurationSource()View on GitHub (pinned to 3a2006ef56)