Humanizr/Humanizer · error · InvalidOperationException

'{path}' contains a sameRenderedAs cycle at '{node}'.

Error message

'{path}' contains a sameRenderedAs cycle at '{node}'.

What it means

Thrown by ResolveUnit when following a chain of sameRenderedAs references creates a cycle. The resolver maintains a HashSet of visited case/unit nodes (formatted as caseName/unitName) and throws when it cannot add a node because it was already in the resolving set.

Source

Thrown at src/Humanizer.SourceGenerators/Common/DurationCaseModels.cs:974

            string caseName,
            string unitName,
            ImmutableArray<string> inventory,
            ImmutableDictionary<string, DurationCaseRealization> realizations,
            HashSet<string> resolving,
            string path,
            bool aliasing)
        {
            if (!inventory.Contains(caseName, StringComparer.Ordinal) ||
                !realizations.TryGetValue(caseName, out var realization))
            {
                throw new InvalidOperationException(
                    $"'{path}' references absent case '{caseName}'.");
            }

            var node = $"{caseName}/{unitName}";
            if (!resolving.Add(node))
            {
                throw new InvalidOperationException(
                    $"'{path}' contains a sameRenderedAs cycle at '{node}'.");
            }

            DurationCaseUnit result;
            switch (realization.Kind)
            {
                case DurationCaseRealizationKind.Authored:
                    if (realization.Units.IsEmpty)
                    {
                        result = new DurationCaseUnit(DurationCaseUnitKind.SameAsNominative, null);
                        break;
                    }

                    var unit = realization.Units[unitName];
                    result = unit.Kind switch
                    {
                        DurationCaseRealizationKind.Authored =>
                            new DurationCaseUnit(DurationCaseUnitKind.Phrase, unit.Phrase),

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Trace the sameRenderedAs chain starting from the node reported in the error.
  2. Break the cycle by ensuring at least one case in the chain resolves to an authored realization (phrase, notApplicable, or unsupported) rather than another sameRenderedAs.
  3. Rebuild to confirm the resolver terminates.

Example fix

# before (cycle: accusative -> dative -> accusative)
accusative:
  sameRenderedAs: dative
dative:
  sameRenderedAs: accusative
# after
accusative:
  sameRenderedAs: nominative
dative:
  sameRenderedAs: nominative
Defensive patterns

Strategy: validation

Validate before calling

# Detect sameRenderedAs cycles via graph traversal before building
# python: build a directed graph of case -> sameRenderedAs target, detect cycles with DFS

Prevention

When it happens

Trigger: Case A's sameRenderedAs points to Case B, and Case B's sameRenderedAs points back to Case A (directly or through a longer chain), so the recursive resolution revisits a node already in the resolving set.

Common situations: A contributor sets up a group of cases to alias each other and accidentally creates a circular reference (e.g. dative -> accusative -> dative). Also occurs when a case is supposed to alias nominative but is itself aliased by nominative.

Related errors


AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13). Data as JSON: /api/errors/64059783a0ce9c2e. Report an issue: GitHub.