OrchardCMS/OrchardCore · error · InvalidOperationException

Buffer has already been rendered

Error message

Buffer has already been rendered

What it means

ViewBufferTextWriterContent wraps pooled ViewBufferTextWriter builders. Once the content has been written to a final TextWriter (rendered), the internal builder is released back to the pool and set to null. Writing or serializing the same content again is invalid, so WriteTo throws to prevent use-after-return of pooled memory.

Solutions

  1. Render the content once per output writer; re-execute the Liquid template or shape for each additional output
  2. If caching is needed, cache the rendered string (e.g. stringWriter output) rather than the IHtmlContent buffer
  3. Check for code paths that serialize content more than once (logging + response writing) and remove the duplicate

Example fix

// before
_htmlContentCache[key] = liquidResult; // rendered once later twice
// after
_htmlContentCache[key] = htmlEncoder.Encode-free string rendered via StringWriter;
var sw = new StringWriter();
liquidResult.WriteTo(sw, HtmlEncoder.Default);
_htmlContentCache[key] = sw.ToString();
Defensive patterns

Strategy: try-catch

Validate before calling

bool canWrite = content is ViewBufferTextWriterContent v && v.ToString() != null; // prefer avoiding re-use entirely: cache strings instead of buffers

Type guard

bool IsRenderedOnce(IHtmlContent c) => c is ViewBufferTextWriterContent vbc && vbc.GetType().GetProperty("_builder", BindingFlags.NonPublic | BindingFlags.Instance) == null;

Try / catch

try { content.WriteTo(writer, encoder); } catch (InvalidOperationException ex) when (ex.Message == "Buffer has already been rendered") { // re-render instead of reusing _logger.LogWarning("Buffer re-use detected; re-rendering"); content = await RenderFreshAsync(); content.WriteTo(writer, encoder); }

Prevention

When it happens

Trigger: Calling WriteTo(writer, encoder) — typically from HtmlString/serialization code — on a ViewBufferTextWriterContent instance that was already rendered once; re-rendering a cached liquid shape/tagBuilder output; holding onto the IHtmlContent produced by a Liquid expression and re-emitting it.

Common situations: Caching an IHtmlContent result (e.g. in a dictionary or memoized tag helper) and writing it twice; double-rendering the same shape in a layout and a partial; async races where a render is attempted while/after the buffer was flushed.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at src/OrchardCore/OrchardCore.DisplayManagement.Liquid/ViewBufferTextWriterContent.cs:212

    public override void Write(StringBuilder value)
    {
        if (value != null)
        {
            foreach (var chunk in value.GetChunks())
            {
                if (!chunk.IsEmpty)
                {
                    Write(chunk.Span);
                }
            }
        }
    }

    public void WriteTo(TextWriter writer, HtmlEncoder encoder)
    {
        if (_builder == null)
        {
            throw new InvalidOperationException("Buffer has already been rendered");
        }

        if (_previousPooledBuilders != null)
        {
            foreach (var pooledBuilder in _previousPooledBuilders)
            {
                foreach (var chunk in pooledBuilder.Builder.GetChunks())
                {
                    if (!chunk.IsEmpty)
                    {
                        writer.Write(chunk.Span);
                    }
                }
            }
        }

        foreach (var chunk in _builder.GetChunks())
        {

View on GitHub (pinned to 4306c0717f)