dotnet/wpf · error · ArgumentException
PropertyOfClassCannotBeNull (CultureInfo, TextRunProperties)
Error message
PropertyOfClassCannotBeNull (CultureInfo, TextRunProperties)
What it means
FetchTextRun maps properties.CultureInfo through CultureMapper.GetSpecificCulture and throws ArgumentException(SR.PropertyOfClassCannotBeNull with CultureInfo/TextRunProperties) when the resulting specific culture is null. A concrete culture is required for shaping, measurement, and line-breaking.
Solutions
- Set CultureInfo to a specific culture (e.g. new CultureInfo("en-US")) on the run properties
- Use CultureInfo.InvariantCulture-derived specific culture or CultureInfo.CurrentUICulture if neutral
- Pre-validate with CultureMapper.GetSpecificCulture in your TextSource before returning the run
Example fix
// before
props.CultureInfo = new CultureInfo("en"); // neutral
// after
props.CultureInfo = new CultureInfo("en-US"); // specific Defensive patterns
Strategy: validation
Validate before calling
var specific = CultureMapper.GetSpecificCulture(run.Properties.CultureInfo);
if (specific == null) run.Properties.CultureInfo = new CultureInfo("en-US"); Type guard
bool HasSpecificCulture(TextRunProperties p) => CultureMapper.GetSpecificCulture(p.CultureInfo) != null;
Try / catch
try { formatter.FormatLine(...); } catch (ArgumentException) { /* set a specific culture and retry */ } Prevention
- Always assign specific (not neutral) cultures to run properties
- Default CultureInfo from the thread's CurrentCulture when unspecified
When it happens
Trigger: TextRun.Properties.CultureInfo is null or a neutral culture (e.g. "en" not "en-US") that CultureMapper cannot map to a specific culture.
Common situations: Threads with no culture set (CultureInfo.CurrentCulture null in defaults); assigning neutral cultures to runs; deserialized properties losing culture info.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- PropertyOfClassCannotBeNull (Typeface, TextRunProperties)
- SR.TextRunPropertiesCannotBeNull
- Animation_ChildMustBeKeyFrame
- Animation_DependencyPropertyIsNotAnimatable
- Animation_UnrecognizedHandoffBehavior
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/589a7defdf619300.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/TextFormatting/TextRunCacheImp.cs:128
if (plsrun == Plsrun.Text || plsrun == Plsrun.InlineObject)
{
TextRunProperties properties = textRun.Properties;
if (properties == null)
throw new ArgumentException(SR.TextRunPropertiesCannotBeNull);
if (properties.FontRenderingEmSize <= 0)
throw new ArgumentException(SR.Format(SR.PropertyOfClassMustBeGreaterThanZero, "FontRenderingEmSize", "TextRunProperties"));
double realMaxFontRenderingEmSize = Constants.RealInfiniteWidth / Constants.GreatestMutiplierOfEm;
if (properties.FontRenderingEmSize > realMaxFontRenderingEmSize)
throw new ArgumentException(SR.Format(SR.PropertyOfClassCannotBeGreaterThan, "FontRenderingEmSize", "TextRunProperties", realMaxFontRenderingEmSize));
CultureInfo culture = CultureMapper.GetSpecificCulture(properties.CultureInfo);
if (culture == null)
throw new ArgumentException(SR.Format(SR.PropertyOfClassCannotBeNull, "CultureInfo", "TextRunProperties"));
if (properties.Typeface == null)
throw new ArgumentException(SR.Format(SR.PropertyOfClassCannotBeNull, "Typeface", "TextRunProperties"));
}
//
// TextRun is specifial to SpanVector because TextRun also encodes position which needs to be
// consistent with the positions encoded by SpanVector. In run cache, the begining of a span
// should always correspond to the begining of a cached text run. If the end of the currently fetched
// run overlaps with the begining of an already cached run, the begining of the cached run needs to be
// adjusted as well as its span. Because we can't gurantee the correctness of the overlapped range
// so we'll simply remove the overlapped runs here.
//
// Move the rider to the end of the current run
textRunSpanRider.At(cpFetch + textRun.Length - 1);
_latestPosition = textRunSpanRider.SpanPosition;View on GitHub (pinned to 81131a70a4)