         trailersBuffer.writeInteger(UInt8(0x80))
         trailersBuffer.writeInteger(UInt32(textTrailers.utf8.count))
         trailersBuffer.writeString(textTrailers)
-        self.responseTextBuffers.append(trailersBuffer)
-
-        // The '!' is fine, we know it's not empty since we just added a buffer.
-        var responseTextBuffer = self.responseTextBuffers.popFirst()!
+        self.appendResponseText(trailersBuffer)
+
+        // This code can only be called on the grpc-web path, so we know the response text buffers must be non-nil
+        // and must contain at least one element.
+        guard var buffers = self.responseTextBuffers else {
+          preconditionFailure("Building web response text but responseTextBuffers are nil")
+        }
+
+        // Avoid a CoW
+        self.responseTextBuffers = nil
+        var responseTextBuffer = buffers.popFirst()!
 
         // Read the data from the first buffer.
         var accumulatedData = responseTextBuffer.readData(length: responseTextBuffer.readableBytes)!
 
         // Reserve enough capacity and append the remaining buffers.
-        let requiredExtraCapacity = self.responseTextBuffers.lazy.map { $0.readableBytes }.reduce(0, +)
+        let requiredExtraCapacity = buffers.lazy.map { $0.readableBytes }.reduce(0, +)
         accumulatedData.reserveCapacity(accumulatedData.count + requiredExtraCapacity)
-        while let buffer = self.responseTextBuffers.popFirst() {
+        while let buffer = buffers.popFirst() {
           accumulatedData.append(contentsOf: buffer.readableBytesView)
         }
+
+        // Restore the buffers.
+        self.responseTextBuffers = buffers
 
         // TODO: Binary responses that are non multiples of 3 will end = or == when encoded in
         // base64. Investigate whether this might have any effect on the transport mechanism and

            allocator: context.channel.allocator,
            compressed: messageContext.compressed
          )
          self.responseTextBuffers.append(buffer)

          // Since we stored the written data, mark the write promise as successful so that the
          // ServerStreaming provider continues sending the data.

  // TODO(kaipi): Extract all gRPC Web processing logic into an independent handler only added on
  // the HTTP1.1 pipeline, as it's starting to get in the way of readability.
  private var requestTextBuffer: NIO.ByteBuffer!
  private var responseTextBuffers: CircularBuffer<ByteBuffer>?

  var inboundState = InboundState.expectingHeaders {
    willSet {
