App-vNext/Polly · error · InvalidOperationException
No predicates were configured. There must be at least one pr
Error message
No predicates were configured. There must be at least one predicate added.
What it means
PredicateBuilder<TResult>.Build() throws InvalidOperationException when _predicates is empty. The builder is the fluent API for declaring which outcomes/exceptions a strategy handles; a strategy must handle at least one outcome, so an empty predicate set is rejected at Build() time rather than silently matching nothing.
Source
Thrown at src/Polly.Core/PredicateBuilder.TResult.cs:128
{
comparer ??= EqualityComparer<TResult>.Default;
return HandleResult(r => comparer.Equals(r, result));
}
/// <summary>
/// Builds the predicate.
/// </summary>
/// <returns>An instance of predicate delegate.</returns>
/// <exception cref="InvalidOperationException">Thrown when no predicates were configured using this builder.</exception>
/// <remarks>
/// The returned predicate will return <see langword="true"/> if any of the configured predicates return <see langword="true"/>.
/// Please be aware of the performance penalty if you register too many predicates with this builder. In such case, it's better to create your own predicate
/// manually as a delegate.
/// </remarks>
public Predicate<Outcome<TResult>> Build() => _predicates.Count switch
{
0 => throw new InvalidOperationException("No predicates were configured. There must be at least one predicate added."),
1 => _predicates[0],
_ => CreatePredicate([.. _predicates]),
};
internal Func<TArgs, ValueTask<bool>> Build<TArgs>()
where TArgs : IOutcomeArguments<TResult>
{
var predicate = Build();
return args => new ValueTask<bool>(predicate(args.Outcome));
}
private static Predicate<Outcome<TResult>> CreatePredicate(Predicate<Outcome<TResult>>[] predicates) =>
outcome =>
{
foreach (var predicate in predicates)
{
if (predicate(outcome))View on GitHub (pinned to d0e46bdb1e)
Solutions
- Add at least one predicate via .Handle<ExceptionType>(), .HandleResult(predicate), or .Of<TResult>() before calling Build().
- Prefer the PredicateBuilder fluent chain inline at the options site so the empty case is visually obvious.
- If you genuinely want to handle nothing, do not add the strategy at all rather than passing an empty predicate.
Example fix
// before
var predicates = new PredicateBuilder<HttpResponseMessage>();
options.ShouldHandle = predicates.Build(); // throws
// after
var predicates = new PredicateBuilder<HttpResponseMessage>()
.Handle<HttpRequestException>()
.HandleResult(r => r.StatusCode >= System.Net.HttpStatusCode.InternalServerError);
options.ShouldHandle = predicates.Build(); Defensive patterns
Strategy: validation
Validate before calling
// Guard the builder before Build():
if (builder == null || /* no Handle added */) {
throw new InvalidOperationException("Add at least one predicate before building.");
}
// Or check the strategy: most *StrategyOptions have a non-null default ShouldHandle, so
// only call builder.Build() when you have explicitly added predicates. Try / catch
try { var predicate = builder.Build(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("No predicates")) {
// add a default predicate and rebuild, or skip the strategy
} Prevention
- Always chain at least one .Handle<>()/.HandleResult()/.Of<T>() before .Build().
- Build predicates inline at the options site so an empty chain is visible.
- If conditional predicate construction can yield zero, fall back to a sensible default predicate.
When it happens
Trigger: Constructing a PredicateBuilder, never calling .Handle<TException>(), .HandleResult(...), .Of<T>() etc., and then assigning builder.Build() to RetryStrategyOptions.ShouldHandle, then Build()ing the pipeline.
Common situations: Building predicates conditionally where every branch is skipped; refactoring that deletes the only .Handle call; copy-pasting a builder setup and forgetting the predicate lines.
Related errors
- This instance of 'CircuitBreakerStateProvider' is already in
- {PrimaryMessage}\nValidation Errors:\n{error.ErrorMessage}
- TaskExecution is not initialized.
- Unable to accept outcome for a task that is not completed.
- Unable to find a resilience pipeline associated with the key
AI-assisted analysis of App-vNext/Polly@d0e46bdb1e (2026-08-13).
Data as JSON: /api/errors/e902daaca9f0f14c.
Report an issue: GitHub.