OrchardCMS/OrchardCore · error · InvalidOperationException

An implementation of 'LiquidTemplateContext' is required

Error message

An implementation of 'LiquidTemplateContext' is required

What it means

The `consent` Liquid value needs the browser's ITrackingConsentFeature, resolved through LiquidTemplateContext.Services -> IHttpContextAccessor -> HttpContext.Features. A non-Liquid TemplateContext fails the cast and throws this InvalidOperationException; note that even with a correct context, GetTrackingFeature may still return null when the feature is absent (HttpContext null or no consent feature registered).

Solutions

  1. Render with a LiquidTemplateContext carrying the scoped service provider.
  2. Render through Orchard's ILiquidTemplateManager / Liquid view pipeline instead of raw Fluid APIs.
  3. In C# code outside templates, get ITrackingConsentFeature from IHttpContextAccessor.HttpContext.Features directly and null-check it.

Example fix

// before
var context = new TemplateContext();
var out = await template.RenderAsync(context);
// after
var context = new LiquidTemplateContext(sp, memberAccessor);
var out = await template.RenderAsync(context);
Defensive patterns

Strategy: type-guard

Validate before calling

if (context is not LiquidTemplateContext) throw new InvalidOperationException("consent value requires a LiquidTemplateContext");

Type guard

bool IsLiquidContext(TemplateContext ctx) => ctx is LiquidTemplateContext;

Try / catch

try { var html = await template.RenderAsync(ctx); } catch (InvalidOperationException ex) when (ex.Message.Contains("LiquidTemplateContext")) { /* create LiquidTemplateContext; also null-check the consent feature */ }

Prevention

When it happens

Trigger: Evaluating `{{ consent }}` or its members (e.g. `{{ consent.track }}`, `{{ consent.granted }}`) in a template rendered with a base Fluid TemplateContext.

Common situations: Custom rendering of Liquid templates in workers/tests without LiquidTemplateContext; privacy/cookie-consent snippets evaluated by an external template host; missing cookie-consent feature combined with the cast guard in custom pipelines.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/f0183d9ec9a9e799. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore/OrchardCore.DisplayManagement.Liquid/Values/TrackingConsentValue.cs:68

        {
            return NilValue.Instance;
        }

        return name switch
        {
            nameof(ITrackingConsentFeature.CanTrack) => BooleanValue.Create(feature.CanTrack),
            nameof(ITrackingConsentFeature.HasConsent) => BooleanValue.Create(feature.HasConsent),
            nameof(ITrackingConsentFeature.IsConsentNeeded) => BooleanValue.Create(feature.IsConsentNeeded),
            "CookieName" => new StringValue(GetCookiePolicyOptions(context)?.ConsentCookie?.Name ?? string.Empty),
            "CookieValue" => new StringValue(GetCookiePolicyOptions(context)?.ConsentCookieValue ?? string.Empty),
            _ => NilValue.Instance
        };
    }

    private static ITrackingConsentFeature? GetTrackingFeature(TemplateContext context)
    {
        var ctx = context as LiquidTemplateContext
            ?? throw new InvalidOperationException($"An implementation of '{nameof(LiquidTemplateContext)}' is required");

        var httpContext = ctx.Services.GetRequiredService<IHttpContextAccessor>().HttpContext;

        return httpContext?.Features.Get<ITrackingConsentFeature>();
    }

    private static CookiePolicyOptions? GetCookiePolicyOptions(TemplateContext context)
    {
        var ctx = context as LiquidTemplateContext
            ?? throw new InvalidOperationException($"An implementation of '{nameof(LiquidTemplateContext)}' is required");

        return ctx.Services.GetService<IOptions<CookiePolicyOptions>>()?.Value;
    }
}

View on GitHub (pinned to 4306c0717f)