Humanizr/Humanizer · error · NotSupportedException
Custom strategy type '{GetType().FullName}' must explicitly
Error message
Custom strategy type '{GetType().FullName}' must explicitly implement IGrammaticalCaseTimeSpanHumanizeStrategy to support grammatical-case-aware durations. What it means
Thrown by DefaultTimeSpanHumanizeStrategy's explicit IGrammaticalCaseTimeSpanHumanizeStrategy implementation when the runtime type is a subclass from a different assembly than Humanizer itself. This prevents a derived class from silently inheriting case support without owning the implementation, which could produce incorrect output.
Source
Thrown at src/Humanizer/TimeSpanHumanizeStrategy/DefaultTimeSpanHumanizeStrategy.cs:77
collectionSeparator,
maxFractionalDigits,
roundingMode,
toSymbols);
string IGrammaticalCaseTimeSpanHumanizeStrategy.Humanize(
TimeSpan timeSpan,
int precision,
bool countEmptyUnits,
CultureInfo? culture,
TimeUnit maxUnit,
TimeUnit minUnit,
string? collectionSeparator,
bool toSymbols,
GrammaticalCase grammaticalCase)
{
if (GetType().Assembly != typeof(DefaultTimeSpanHumanizeStrategy).Assembly)
{
throw new NotSupportedException(
$"Custom strategy type '{GetType().FullName}' must explicitly implement {nameof(IGrammaticalCaseTimeSpanHumanizeStrategy)} to support grammatical-case-aware durations.");
}
return TimeSpanHumanizeExtensions.DefaultHumanizeWithCase(
timeSpan,
precision,
countEmptyUnits,
culture,
maxUnit,
minUnit,
collectionSeparator,
toSymbols,
grammaticalCase);
}
}View on GitHub (pinned to ffc2b77c0f)
Solutions
- Implement IGrammaticalCaseTimeSpanHumanizeStrategy explicitly on your subclass instead of inheriting it.
- Compose (wrap) a DefaultTimeSpanHumanizeStrategy rather than subclassing it.
- Use the built-in DefaultTimeSpanHumanizeStrategy directly.
Example fix
// before
public class LoggingStrategy : DefaultTimeSpanHumanizeStrategy { /* inherits case support */ }
Configurator.TimeSpanHumanizeStrategy = new LoggingStrategy();
// after
public class LoggingStrategy : DefaultTimeSpanHumanizeStrategy, IGrammaticalCaseTimeSpanHumanizeStrategy
{
string IGrammaticalCaseTimeSpanHumanizeStrategy.Humanize(
TimeSpan ts, int p, bool c, CultureInfo? cult,
TimeUnit max, TimeUnit min, string? sep, bool sym, GrammaticalCase gc)
{
// log here
return TimeSpanHumanizeExtensions.DefaultHumanizeWithCase(
ts, p, c, cult, max, min, sep, sym, gc);
}
} Defensive patterns
Strategy: type-guard
Validate before calling
static bool IsSafeSubclass(Type strategyType) =>
strategyType == typeof(DefaultTimeSpanHumanizeStrategy) ||
strategyType.GetInterfaces().Contains(typeof(IGrammaticalCaseTimeSpanHumanizeStrategy)) &&
strategyType.Assembly != typeof(DefaultTimeSpanHumanizeStrategy).Assembly; Type guard
static bool StrategyOwnsCaseImpl(ITimeSpanHumanizeStrategy strategy) =>
strategy.GetType() == typeof(DefaultTimeSpanHumanizeStrategy) ||
strategy is IGrammaticalCaseTimeSpanHumanizeStrategy &&
strategy.GetType().GetInterfaces().Contains(typeof(IGrammaticalCaseTimeSpanHumanizeStrategy)); Prevention
- Prefer composition over inheritance for custom strategies.
- If subclassing, re-implement IGrammaticalCaseTimeSpanHumanizeStrategy explicitly.
- Test case-aware humanize after subclassing DefaultTimeSpanHumanizeStrategy.
When it happens
Trigger: Creating a class that extends DefaultTimeSpanHumanizeStrategy in your own assembly, registering it via Configurator.TimeSpanHumanizeStrategy, and calling HumanizeWithCase. The assembly-equality check at line 75 detects the subclass.
Common situations: Developer subclasses DefaultTimeSpanHumanizeStrategy to add logging or caching and then calls case-aware APIs. The subclass inherits the explicit interface implementation, but Humanizer blocks this for safety.
Related errors
- The configured ITimeSpanHumanizeStrategy does not support gr
- Custom formatter type '{GetType().FullName}' must explicitly
- Grammatical case is not supported for time-unit symbols.
- The formatter for '{culture?.Name ?? CultureInfo.CurrentCult
- Culture '{Culture.Name}' has no grammatical-case classificat
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/3f7e2369986bb2cd.
Report an issue: GitHub.