Return null from FaultGenerator when no fault is generated - #3220
Merged
martincostello merged 1 commit intoSep 6, 2026
Merged
Conversation
FaultGenerator's implicit conversion dereferenced the nullable Outcome
returned by the underlying generator via `!.Value`, throwing
InvalidOperationException ("Nullable object must have a value.") when the
generator produced no outcome - an empty generator, or registered weights
summing to zero. ChaosFaultStrategy treats a null fault as "do not inject"
and only catches OperationCanceledException, so the exception escaped and
failed the execution the strategy was meant to leave untouched.
Propagate the nullable outcome with `?.Exception`, mirroring
OutcomeGenerator<TResult>, so no fault is injected when none is generated.
Contributor
Author
|
@dotnet-policy-service agree |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3220 +/- ##
=======================================
Coverage 96.16% 96.16%
=======================================
Files 311 311
Lines 7142 7142
Branches 1006 1007 +1
=======================================
Hits 6868 6868
Misses 221 221
Partials 53 53
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. |
martincostello
approved these changes
Sep 6, 2026
Contributor
|
Thanks for your contribution @dualfroz - the changes from this pull request have been published as part of version 8.8.0 📦, which is now available from NuGet.org 🚀 |
This was referenced Sep 14, 2026
This was referenced Sep 15, 2026
IhateTrains
pushed a commit
to ParadoxGameConverters/ImperatorToCK3
that referenced
this pull request
Sep 16, 2026
Updated [Polly](https://github.com/App-vNext/Polly) from 8.7.0 to 8.8.0. <details> <summary>Release notes</summary> _Sourced from [Polly's releases](https://github.com/App-vNext/Polly/releases)._ ## 8.8.0 ## Highlights * Add `EnableReloadsWithMonitor()` accepting custom `IOptionsMonitor` by @arashzjahangiri in App-vNext/Polly#3140 * Return null from `FaultGenerator` when no fault is generated by @dualfroz in App-vNext/Polly#3220 ## What's Changed * Update workflow timeout by @martincostello in App-vNext/Polly#3106 * Set `SOURCE_DATE_EPOCH` by @martincostello in App-vNext/Polly#3115 * Use `NUGET_API_KEY` by @martincostello in App-vNext/Polly#3116 * Test refactoring by @martincostello in App-vNext/Polly#3132 * Avoid test flakiness by @martincostello in App-vNext/Polly#3134 * Group github/codeql-action updates by @martincostello in App-vNext/Polly#3147 * Fix wrong word in hedging documentation by @latent-9 in App-vNext/Polly#3191 * OSMF sponsorship notice by @joelhulen in App-vNext/Polly#3194 * Fix XML doc comment on `PipelineExecutedArguments` by @JiuYue0820 in App-vNext/Polly#3201 * Update to xunit v3 by @martincostello in App-vNext/Polly#3131 * .NET 11 preparation by @martincostello in App-vNext/Polly#3225 * Fix flaky test by @martincostello in App-vNext/Polly#3231 ## New Contributors * @arashzjahangiri made their first contribution in App-vNext/Polly#3140 * @latent-9 made their first contribution in App-vNext/Polly#3191 * @JiuYue0820 made their first contribution in App-vNext/Polly#3201 * @dualfroz made their first contribution in App-vNext/Polly#3220 **Full Changelog**: App-vNext/Polly@8.7.0...8.8.0 Commits viewable in [compare view](App-vNext/Polly@8.7.0...8.8.0). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The issue or feature being addressed
FaultGeneratorthrowsInvalidOperationException("Nullable object must have avalue.") instead of injecting no fault when its underlying generator yields no
outcome.
The implicit conversion operator on
FaultGenerator(
src/Polly.Core/Simmy/Fault/FaultGenerator.cs) builds its delegate as:generatorDelegateis aFunc<ResilienceContext, Outcome<VoidResult>?>produced byGeneratorHelper<VoidResult>.CreateGenerator(). It legitimately returnsnullwhen:FaultGenerator), in which caseCreateGenerator()returns_ => null; orAddException<T>(weight: 0)), inwhich case the selection loop matches no factory and returns
null.Because
Outcome<TResult>is areadonly struct,Outcome<VoidResult>?is aNullable<Outcome<VoidResult>>. The null-forgiving!operator has no runtime effect,so the subsequent
.Valuedereferences an empty nullable and throwsInvalidOperationException.ChaosFaultStrategy.ExecuteCoreexplicitly treats anullfault as "do not inject"(
if (fault is not null)), and itstryblock only catchesOperationCanceledException, so the exception escapes and fails the user's executionthat the strategy was meant to leave untouched.
The sibling
OutcomeGenerator<TResult>handles the same situation correctly bypropagating the nullable value unchanged
(
src/Polly.Core/Simmy/Outcomes/OutcomeGenerator.cs):Details on the issue fix or feature implementation
Propagate
nullfrom the generator instead of dereferencing it, mirroringOutcomeGenerator<TResult>:When a fault is produced,
Outcome<VoidResult>.Exceptionreturns the same exception asbefore; when the generator yields no outcome, the delegate now returns
nullandChaosFaultStrategyproceeds without injecting a fault.Added two unit tests to
test/Polly.Core.Tests/Simmy/Fault/FaultGeneratorTests.cs:NoExceptionRegistered_ShouldReturnNull- an emptyFaultGeneratorreturnsnull.AllWeightsZero_ShouldReturnNull- a generator whose weights sum to zero returnsnull.Both tests fail on the unmodified code with
System.InvalidOperationException : Nullable object must have a value.atFaultGenerator.cs:81, and pass after the fix.Confirm the following