Make the Nullable<T>.Value exception message actionable - #132367
Make the Nullable<T>.Value exception message actionable#132367jeffhandley wants to merge 3 commits into
Conversation
InvalidOperation_NoValue read "Nullable object must have a value.", which describes an obligation on the object's state rather than the invalid action. That misleads developers into thinking they must assign a value, when the actual fix is to check HasValue before reading Value. Reword it to name the member accessed and the check to perform, following the wording style already used for comparable failures (System.Data.Common SqlMisc_NullValueMessage, System.ComponentModel.Composition ReflectionModel_ExportNotReadable). The resource is used only by Nullable<T>.Value: ThrowHelper.ThrowInvalidOperationException_InvalidOperation_NoValue (called solely from the Value getter) and the NativeAOT reflection-invoke shim in CustomMethodMapper.Nullable. Both resx definitions are updated. No test asserts the text. Per docs/coding-guidelines/breaking-change-rules.md, changing the text of an error message is not a breaking change. Fixes #126560 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Updates the InvalidOperation_NoValue resource string to make the Nullable<T>.Value failure message describe the invalid action (reading Value when there is no value) and provide a concrete next step (check HasValue) while keeping the resource key unchanged across CoreLib and NativeAOT.
Changes:
- Replaces the
InvalidOperation_NoValuetext inSystem.Private.CoreLibresources with an actionable message referencingValue/HasValue. - Mirrors the same updated text in the NativeAOT
System.Private.Reflection.Executionresources to keep behavior consistent across runtimes.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/libraries/System.Private.CoreLib/src/Resources/Strings.resx | Updates InvalidOperation_NoValue message used by Nullable<T>.Value via ThrowHelper. |
| src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resx | Updates the NativeAOT InvalidOperation_NoValue message used by the reflection invoke shim for Nullable<T>.Value. |
|
Tagging subscribers to this area: @dotnet/area-system-runtime |
…rings.resx Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resx:125
- This change removes multiple unrelated resource entries (e.g., Arg_EmptyArray, MissingMember, InvalidOperation_NoValue) from this .resx. Even if they were unused, this is a large scope expansion for a PR that appears to be about improving Nullable.Value’s exception message, and it makes it harder to review/attribute behavioral changes. Please revert these unrelated deletions (or split them into a separate cleanup PR with rationale).
<data name="Reflection_InsufficientMetadata_EdbNeeded" xml:space="preserve">
<value>'{0}' is missing native code or metadata. This can happen for code that is not compatible with trimming or AOT. Inspect and fix trimming and AOT related warnings that were generated when the app was published. For more information see https://aka.ms/nativeaot-compatibility</value>
</data>
<data name="Arg_HTCapacityOverflow" xml:space="preserve">
<value>Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table.</value>
</data>
…on.Execution Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resx:124
- The PR description says the NativeAOT System.Private.Reflection.Execution Strings.resx definition for InvalidOperation_NoValue is updated, but this diff instead deletes that entry (and several other resource keys). If the extra deletions are intentional cleanup, please split them into a separate PR and/or update the PR description; otherwise, consider restoring the removed resource entries.
<data name="Reflection_InsufficientMetadata_EdbNeeded" xml:space="preserve">
<value>'{0}' is missing native code or metadata. This can happen for code that is not compatible with trimming or AOT. Inspect and fix trimming and AOT related warnings that were generated when the app was published. For more information see https://aka.ms/nativeaot-compatibility</value>
</data>
<data name="RFLCT_Targ_StatFldReqTarg" xml:space="preserve">
<value>Non-static field requires a target.</value>
src/coreclr/nativeaot/System.Private.Reflection.Execution/src/System.Private.Reflection.Execution.csproj:70
- This PR is described as a targeted update to the Nullable.Value exception message, but it also removes the linked HashHelpers.cs compile item from System.Private.Reflection.Execution. Even if unused, this is an unrelated change that makes the PR harder to review/safely backport; consider reverting this in this PR (or documenting why it’s needed).
<Compile Include="$(CompilerCommonPath)\Internal\LowLevelLinq\LowLevelEnumerable.ToArray.cs">
<Link>Internal\LowLevelLinq\LowLevelEnumerable.ToArray.cs</Link>
</Compile>
</ItemGroup>
Fixes #126560
Problem
InvalidOperation_NoValue— the message thrown byNullable<T>.Valuewhen there's no value — reads:As #126560 points out, this states an obligation on the object's state rather than naming the invalid action. It reads as "you must assign a value", when the actual fix is almost always the opposite: don't read
.Valuewithout checking.HasValuefirst. It also never mentions which member was accessed.Change
This follows the wording style already used in the repo for comparable "you called this on something that has no value" failures:
SqlMisc_NullValueMessage(System.Data.Common) — "Data is Null. This method or property cannot be called on Null values."ReflectionModel_ExportNotReadable(System.ComponentModel.Composition) — "Cannot get the value of property '{0}', because the member is not readable. The property must have an accessible getter."NoResultOnFailed(System.Text.RegularExpressions) — "Result cannot be called on a failed Match."The issue also floated "Cannot read the Value property of a null Nullable object." I avoided "null Nullable object" because
Nullable<T>is a struct and is nevernull— "that has no value" keeps the same cadence without the imprecision. The second sentence names the check to perform, per "DO ensure that exception messages are clear and actionable" (docs/coding-guidelines/framework-design-guidelines-digest.md).The resource name is unchanged.
Scope
InvalidOperation_NoValueis not shared with any unrelated call site — all definitions and references areNullable<T>.Valuesemantics:src/libraries/System.Private.CoreLib/src/Resources/Strings.resxsrc/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resxThrowHelper.ThrowInvalidOperationException_InvalidOperation_NoValue— sole caller is theNullable<T>.ValuegetterCustomMethodMapper.Nullable.cs— NativeAOT reflection-invoke shim forNullable<T>.ValueBoth
.resxdefinitions are updated. Mono shares the libraries CoreLib.resx; there is no third copy. No test asserts the text, and the NativeAOTFrameworkStringsstring-pinning smoke test pins other resources.Per
docs/coding-guidelines/breaking-change-rules.md, changing the text of an error message is not a breaking change ("users should not rely on these text messages, and they change anyways based on culture").Validation
System.Private.CoreLib(build.cmd clr.corelib -c Release) — clean.System.Private.CoreLib.dllthat the new string is embedded and the old one is gone, soSR.InvalidOperation_NoValuestill resolves.System.Private.Reflection.Execution.resxwas not compiled locally — that project currently fails on a pre-existing missing generatedAsmOffsets.csrequiring a full NativeAOT native build. The edit there is identical in shape and doesn't change the resource name. CI covers it.Note
This pull request description was generated by GitHub Copilot.