Refactor QPACK encoder and decoder stream errors.

This CL introduces some unused arguments.  The intention is to do as much
refactoring as possible before cl/337315008, with the goal to make both CLs
easier to review.

No behavioral change in production code.

Add some extra information to logs in QPACK roundtrip fuzzer and offline
decoder.

Add QpackEncoderStreamReceiverTest.InvalidHuffmanEncoding and
QpackInstructionDecoderTest.StringLiteralTooLong.

PiperOrigin-RevId: 337654909
Change-Id: Ie7fa54abf264d6906ffd63ef886184c70df3d08d
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 2d883b0..ac05d0f 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -476,19 +476,21 @@
       max_inbound_header_list_size_;
 }
 
-void QuicSpdySession::OnDecoderStreamError(absl::string_view error_message) {
+void QuicSpdySession::OnDecoderStreamError(QuicErrorCode error_code,
+                                           absl::string_view error_message) {
   DCHECK(VersionUsesHttp3(transport_version()));
 
   CloseConnectionWithDetails(
-      QUIC_QPACK_DECODER_STREAM_ERROR,
+      error_code,
       quiche::QuicheStrCat("Decoder stream error: ", error_message));
 }
 
-void QuicSpdySession::OnEncoderStreamError(absl::string_view error_message) {
+void QuicSpdySession::OnEncoderStreamError(QuicErrorCode error_code,
+                                           absl::string_view error_message) {
   DCHECK(VersionUsesHttp3(transport_version()));
 
   CloseConnectionWithDetails(
-      QUIC_QPACK_ENCODER_STREAM_ERROR,
+      error_code,
       quiche::QuicheStrCat("Encoder stream error: ", error_message));
 }
 
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 2a6ca31..e3a3f79 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -154,10 +154,12 @@
   void Initialize() override;
 
   // QpackEncoder::DecoderStreamErrorDelegate implementation.
-  void OnDecoderStreamError(absl::string_view error_message) override;
+  void OnDecoderStreamError(QuicErrorCode error_code,
+                            absl::string_view error_message) override;
 
   // QpackDecoder::EncoderStreamErrorDelegate implementation.
-  void OnEncoderStreamError(absl::string_view error_message) override;
+  void OnEncoderStreamError(QuicErrorCode error_code,
+                            absl::string_view error_message) override;
 
   // Called by |headers_stream_| when headers with a priority have been
   // received for a stream.  This method will only be called for server streams.
diff --git a/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc b/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc
index 2139418..3c33cd7 100644
--- a/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc
+++ b/quic/core/qpack/fuzzer/qpack_decoder_fuzzer.cc
@@ -30,7 +30,8 @@
   ErrorDelegate(bool* error_detected) : error_detected_(error_detected) {}
   ~ErrorDelegate() override = default;
 
-  void OnEncoderStreamError(absl::string_view /*error_message*/) override {
+  void OnEncoderStreamError(QuicErrorCode /*error_code*/,
+                            absl::string_view /*error_message*/) override {
     *error_detected_ = true;
   }
 
diff --git a/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc b/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc
index ce2ef54..710450e 100644
--- a/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc
+++ b/quic/core/qpack/fuzzer/qpack_encoder_stream_receiver_fuzzer.cc
@@ -29,7 +29,8 @@
                                     absl::string_view /*value*/) override {}
   void OnDuplicate(uint64_t /*index*/) override {}
   void OnSetDynamicTableCapacity(uint64_t /*capacity*/) override {}
-  void OnErrorDetected(absl::string_view /*error_message*/) override {
+  void OnErrorDetected(QuicErrorCode /*error_code*/,
+                       absl::string_view /*error_message*/) override {
     error_detected_ = true;
   }
 
diff --git a/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc b/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
index c14add3..0cf022c 100644
--- a/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
+++ b/quic/core/qpack/fuzzer/qpack_round_trip_fuzzer.cc
@@ -15,6 +15,7 @@
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_stream_sender_delegate.h"
 #include "net/third_party/quiche/src/quic/core/qpack/value_splitting_header_list.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_containers.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h"
 #include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.h"
@@ -64,8 +65,9 @@
    public:
     ~CrashingDecoderStreamErrorDelegate() override = default;
 
-    void OnDecoderStreamError(absl::string_view error_message) override {
-      CHECK(false) << error_message;
+    void OnDecoderStreamError(QuicErrorCode error_code,
+                              absl::string_view error_message) override {
+      CHECK(false) << QuicErrorCodeToString(error_code) << " " << error_message;
     }
   };
 
@@ -377,8 +379,9 @@
    public:
     ~CrashingEncoderStreamErrorDelegate() override = default;
 
-    void OnEncoderStreamError(absl::string_view error_message) override {
-      CHECK(false) << error_message;
+    void OnEncoderStreamError(QuicErrorCode error_code,
+                              absl::string_view error_message) override {
+      CHECK(false) << QuicErrorCodeToString(error_code) << " " << error_message;
     }
   };
 
diff --git a/quic/core/qpack/qpack_decoder.cc b/quic/core/qpack/qpack_decoder.cc
index 9268c9f..67d6a4e 100644
--- a/quic/core/qpack/qpack_decoder.cc
+++ b/quic/core/qpack/qpack_decoder.cc
@@ -74,15 +74,15 @@
   if (is_static) {
     auto entry = header_table_.LookupEntry(/* is_static = */ true, name_index);
     if (!entry) {
-      encoder_stream_error_delegate_->OnEncoderStreamError(
-          "Invalid static table entry.");
+      OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                      "Invalid static table entry.");
       return;
     }
 
     entry = header_table_.InsertEntry(entry->name(), value);
     if (!entry) {
-      encoder_stream_error_delegate_->OnEncoderStreamError(
-          "Error inserting entry with name reference.");
+      OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                      "Error inserting entry with name reference.");
     }
     return;
   }
@@ -90,22 +90,21 @@
   uint64_t absolute_index;
   if (!QpackEncoderStreamRelativeIndexToAbsoluteIndex(
           name_index, header_table_.inserted_entry_count(), &absolute_index)) {
-    encoder_stream_error_delegate_->OnEncoderStreamError(
-        "Invalid relative index.");
+    OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR, "Invalid relative index.");
     return;
   }
 
   const QpackEntry* entry =
       header_table_.LookupEntry(/* is_static = */ false, absolute_index);
   if (!entry) {
-    encoder_stream_error_delegate_->OnEncoderStreamError(
-        "Dynamic table entry not found.");
+    OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                    "Dynamic table entry not found.");
     return;
   }
   entry = header_table_.InsertEntry(entry->name(), value);
   if (!entry) {
-    encoder_stream_error_delegate_->OnEncoderStreamError(
-        "Error inserting entry with name reference.");
+    OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                    "Error inserting entry with name reference.");
   }
 }
 
@@ -113,8 +112,8 @@
                                                 absl::string_view value) {
   const QpackEntry* entry = header_table_.InsertEntry(name, value);
   if (!entry) {
-    encoder_stream_error_delegate_->OnEncoderStreamError(
-        "Error inserting literal entry.");
+    OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                    "Error inserting literal entry.");
   }
 }
 
@@ -122,34 +121,35 @@
   uint64_t absolute_index;
   if (!QpackEncoderStreamRelativeIndexToAbsoluteIndex(
           index, header_table_.inserted_entry_count(), &absolute_index)) {
-    encoder_stream_error_delegate_->OnEncoderStreamError(
-        "Invalid relative index.");
+    OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR, "Invalid relative index.");
     return;
   }
 
   const QpackEntry* entry =
       header_table_.LookupEntry(/* is_static = */ false, absolute_index);
   if (!entry) {
-    encoder_stream_error_delegate_->OnEncoderStreamError(
-        "Dynamic table entry not found.");
+    OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                    "Dynamic table entry not found.");
     return;
   }
   entry = header_table_.InsertEntry(entry->name(), entry->value());
   if (!entry) {
-    encoder_stream_error_delegate_->OnEncoderStreamError(
-        "Error inserting duplicate entry.");
+    OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                    "Error inserting duplicate entry.");
   }
 }
 
 void QpackDecoder::OnSetDynamicTableCapacity(uint64_t capacity) {
   if (!header_table_.SetDynamicTableCapacity(capacity)) {
-    encoder_stream_error_delegate_->OnEncoderStreamError(
-        "Error updating dynamic table capacity.");
+    OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                    "Error updating dynamic table capacity.");
   }
 }
 
-void QpackDecoder::OnErrorDetected(absl::string_view error_message) {
-  encoder_stream_error_delegate_->OnEncoderStreamError(error_message);
+void QpackDecoder::OnErrorDetected(QuicErrorCode /* error_code */,
+                                   absl::string_view error_message) {
+  encoder_stream_error_delegate_->OnEncoderStreamError(
+      QUIC_QPACK_ENCODER_STREAM_ERROR, error_message);
 }
 
 std::unique_ptr<QpackProgressiveDecoder> QpackDecoder::CreateProgressiveDecoder(
diff --git a/quic/core/qpack/qpack_decoder.h b/quic/core/qpack/qpack_decoder.h
index c950e87..6e0c0ab 100644
--- a/quic/core/qpack/qpack_decoder.h
+++ b/quic/core/qpack/qpack_decoder.h
@@ -14,6 +14,7 @@
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
@@ -22,6 +23,10 @@
 // QPACK decoder class.  Exactly one instance should exist per QUIC connection.
 // This class vends a new QpackProgressiveDecoder instance for each new header
 // list to be encoded.
+// QpackProgressiveDecoder detects and signals errors with header blocks, which
+// are stream errors.
+// The only input of QpackDecoder is the encoder stream.  Any error QpackDecoder
+// signals is an encoder stream error, which is fatal to the connection.
 class QUIC_EXPORT_PRIVATE QpackDecoder
     : public QpackEncoderStreamReceiver::Delegate,
       public QpackProgressiveDecoder::BlockedStreamLimitEnforcer,
@@ -34,7 +39,8 @@
    public:
     virtual ~EncoderStreamErrorDelegate() {}
 
-    virtual void OnEncoderStreamError(absl::string_view error_message) = 0;
+    virtual void OnEncoderStreamError(QuicErrorCode error_code,
+                                      absl::string_view error_message) = 0;
   };
 
   QpackDecoder(uint64_t maximum_dynamic_table_capacity,
@@ -84,7 +90,8 @@
                                     absl::string_view value) override;
   void OnDuplicate(uint64_t index) override;
   void OnSetDynamicTableCapacity(uint64_t capacity) override;
-  void OnErrorDetected(absl::string_view error_message) override;
+  void OnErrorDetected(QuicErrorCode error_code,
+                       absl::string_view error_message) override;
 
   // delegate must be set if dynamic table capacity is not zero.
   void set_qpack_stream_sender_delegate(QpackStreamSenderDelegate* delegate) {
diff --git a/quic/core/qpack/qpack_decoder_stream_receiver.cc b/quic/core/qpack/qpack_decoder_stream_receiver.cc
index a0dcb62..528fb1f 100644
--- a/quic/core/qpack/qpack_decoder_stream_receiver.cc
+++ b/quic/core/qpack/qpack_decoder_stream_receiver.cc
@@ -43,11 +43,12 @@
   return true;
 }
 
-void QpackDecoderStreamReceiver::OnError(absl::string_view error_message) {
+void QpackDecoderStreamReceiver::OnInstructionDecodingError(
+    absl::string_view error_message) {
   DCHECK(!error_detected_);
 
   error_detected_ = true;
-  delegate_->OnErrorDetected(error_message);
+  delegate_->OnErrorDetected(QUIC_QPACK_DECODER_STREAM_ERROR, error_message);
 }
 
 }  // namespace quic
diff --git a/quic/core/qpack/qpack_decoder_stream_receiver.h b/quic/core/qpack/qpack_decoder_stream_receiver.h
index 51e0d3c..c20306a 100644
--- a/quic/core/qpack/qpack_decoder_stream_receiver.h
+++ b/quic/core/qpack/qpack_decoder_stream_receiver.h
@@ -10,6 +10,7 @@
 #include "absl/strings/string_view.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_stream_receiver.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
@@ -34,7 +35,8 @@
     // 5.3.3 Stream Cancellation
     virtual void OnStreamCancellation(QuicStreamId stream_id) = 0;
     // Decoding error
-    virtual void OnErrorDetected(absl::string_view error_message) = 0;
+    virtual void OnErrorDetected(QuicErrorCode error_code,
+                                 absl::string_view error_message) = 0;
   };
 
   explicit QpackDecoderStreamReceiver(Delegate* delegate);
@@ -51,7 +53,7 @@
 
   // QpackInstructionDecoder::Delegate implementation.
   bool OnInstructionDecoded(const QpackInstruction* instruction) override;
-  void OnError(absl::string_view error_message) override;
+  void OnInstructionDecodingError(absl::string_view error_message) override;
 
  private:
   QpackInstructionDecoder instruction_decoder_;
diff --git a/quic/core/qpack/qpack_decoder_stream_receiver_test.cc b/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
index f800e33..44e40fa 100644
--- a/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
+++ b/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
@@ -27,7 +27,7 @@
   MOCK_METHOD(void, OnStreamCancellation, (QuicStreamId stream_id), (override));
   MOCK_METHOD(void,
               OnErrorDetected,
-              (absl::string_view error_message),
+              (QuicErrorCode error_code, absl::string_view error_message),
               (override));
 };
 
@@ -53,7 +53,8 @@
   EXPECT_CALL(delegate_, OnInsertCountIncrement(200));
   stream_.Decode(quiche::QuicheTextUtils::HexDecode("3f8901"));
 
-  EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large.")));
+  EXPECT_CALL(delegate_, OnErrorDetected(QUIC_QPACK_DECODER_STREAM_ERROR,
+                                         Eq("Encoded integer too large.")));
   stream_.Decode(quiche::QuicheTextUtils::HexDecode("3fffffffffffffffffffff"));
 }
 
@@ -70,7 +71,8 @@
   EXPECT_CALL(delegate_, OnHeaderAcknowledgement(503));
   stream_.Decode(quiche::QuicheTextUtils::HexDecode("fff802"));
 
-  EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large.")));
+  EXPECT_CALL(delegate_, OnErrorDetected(QUIC_QPACK_DECODER_STREAM_ERROR,
+                                         Eq("Encoded integer too large.")));
   stream_.Decode(quiche::QuicheTextUtils::HexDecode("ffffffffffffffffffffff"));
 }
 
@@ -87,7 +89,8 @@
   EXPECT_CALL(delegate_, OnStreamCancellation(110));
   stream_.Decode(quiche::QuicheTextUtils::HexDecode("7f2f"));
 
-  EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large.")));
+  EXPECT_CALL(delegate_, OnErrorDetected(QUIC_QPACK_DECODER_STREAM_ERROR,
+                                         Eq("Encoded integer too large.")));
   stream_.Decode(quiche::QuicheTextUtils::HexDecode("7fffffffffffffffffffff"));
 }
 
diff --git a/quic/core/qpack/qpack_decoder_test.cc b/quic/core/qpack/qpack_decoder_test.cc
index 17c134f..c3348e7 100644
--- a/quic/core/qpack/qpack_decoder_test.cc
+++ b/quic/core/qpack/qpack_decoder_test.cc
@@ -441,7 +441,8 @@
 
 TEST_P(QpackDecoderTest, EncoderStreamErrorEntryTooLarge) {
   EXPECT_CALL(encoder_stream_error_delegate_,
-              OnEncoderStreamError(Eq("Error inserting literal entry.")));
+              OnEncoderStreamError(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                   Eq("Error inserting literal entry.")));
 
   // Set dynamic table capacity to 34.
   DecodeEncoderStreamData(quiche::QuicheTextUtils::HexDecode("3f03"));
@@ -451,7 +452,8 @@
 
 TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidStaticTableEntry) {
   EXPECT_CALL(encoder_stream_error_delegate_,
-              OnEncoderStreamError(Eq("Invalid static table entry.")));
+              OnEncoderStreamError(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                   Eq("Invalid static table entry.")));
 
   // Address invalid static table entry index 99.
   DecodeEncoderStreamData(quiche::QuicheTextUtils::HexDecode("ff2400"));
@@ -459,7 +461,8 @@
 
 TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidDynamicTableEntry) {
   EXPECT_CALL(encoder_stream_error_delegate_,
-              OnEncoderStreamError(Eq("Invalid relative index.")));
+              OnEncoderStreamError(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                   Eq("Invalid relative index.")));
 
   DecodeEncoderStreamData(quiche::QuicheTextUtils::HexDecode(
       "3fe107"          // Set dynamic table capacity to 1024.
@@ -471,7 +474,8 @@
 
 TEST_P(QpackDecoderTest, EncoderStreamErrorDuplicateInvalidEntry) {
   EXPECT_CALL(encoder_stream_error_delegate_,
-              OnEncoderStreamError(Eq("Invalid relative index.")));
+              OnEncoderStreamError(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                   Eq("Invalid relative index.")));
 
   DecodeEncoderStreamData(quiche::QuicheTextUtils::HexDecode(
       "3fe107"          // Set dynamic table capacity to 1024.
@@ -483,7 +487,8 @@
 
 TEST_P(QpackDecoderTest, EncoderStreamErrorTooLargeInteger) {
   EXPECT_CALL(encoder_stream_error_delegate_,
-              OnEncoderStreamError(Eq("Encoded integer too large.")));
+              OnEncoderStreamError(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                   Eq("Encoded integer too large.")));
 
   DecodeEncoderStreamData(
       quiche::QuicheTextUtils::HexDecode("3fffffffffffffffffffff"));
@@ -589,7 +594,8 @@
 TEST_P(QpackDecoderTest, TableCapacityMustNotExceedMaximum) {
   EXPECT_CALL(
       encoder_stream_error_delegate_,
-      OnEncoderStreamError(Eq("Error updating dynamic table capacity.")));
+      OnEncoderStreamError(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                           Eq("Error updating dynamic table capacity.")));
 
   // Try to update dynamic table capacity to 2048, which exceeds the maximum.
   DecodeEncoderStreamData(quiche::QuicheTextUtils::HexDecode("3fe10f"));
diff --git a/quic/core/qpack/qpack_encoder.cc b/quic/core/qpack/qpack_encoder.cc
index 30df1df..58df9d0 100644
--- a/quic/core/qpack/qpack_encoder.cc
+++ b/quic/core/qpack/qpack_encoder.cc
@@ -403,29 +403,32 @@
 
 void QpackEncoder::OnInsertCountIncrement(uint64_t increment) {
   if (increment == 0) {
-    decoder_stream_error_delegate_->OnDecoderStreamError(
-        "Invalid increment value 0.");
+    OnErrorDetected(QUIC_QPACK_DECODER_STREAM_ERROR,
+                    "Invalid increment value 0.");
     return;
   }
 
   if (!blocking_manager_.OnInsertCountIncrement(increment)) {
-    decoder_stream_error_delegate_->OnDecoderStreamError(
-        "Insert Count Increment instruction causes overflow.");
+    OnErrorDetected(QUIC_QPACK_DECODER_STREAM_ERROR,
+                    "Insert Count Increment instruction causes overflow.");
   }
 
   if (blocking_manager_.known_received_count() >
       header_table_.inserted_entry_count()) {
-    decoder_stream_error_delegate_->OnDecoderStreamError(quiche::QuicheStrCat(
-        "Increment value ", increment, " raises known received count to ",
-        blocking_manager_.known_received_count(),
-        " exceeding inserted entry count ",
-        header_table_.inserted_entry_count()));
+    OnErrorDetected(
+        QUIC_QPACK_DECODER_STREAM_ERROR,
+        quiche::QuicheStrCat("Increment value ", increment,
+                             " raises known received count to ",
+                             blocking_manager_.known_received_count(),
+                             " exceeding inserted entry count ",
+                             header_table_.inserted_entry_count()));
   }
 }
 
 void QpackEncoder::OnHeaderAcknowledgement(QuicStreamId stream_id) {
   if (!blocking_manager_.OnHeaderAcknowledgement(stream_id)) {
-    decoder_stream_error_delegate_->OnDecoderStreamError(
+    OnErrorDetected(
+        QUIC_QPACK_DECODER_STREAM_ERROR,
         quiche::QuicheStrCat("Header Acknowledgement received for stream ",
                              stream_id, " with no outstanding header blocks."));
   }
@@ -435,8 +438,10 @@
   blocking_manager_.OnStreamCancellation(stream_id);
 }
 
-void QpackEncoder::OnErrorDetected(absl::string_view error_message) {
-  decoder_stream_error_delegate_->OnDecoderStreamError(error_message);
+void QpackEncoder::OnErrorDetected(QuicErrorCode /* error_code */,
+                                   absl::string_view error_message) {
+  decoder_stream_error_delegate_->OnDecoderStreamError(
+      QUIC_QPACK_DECODER_STREAM_ERROR, error_message);
 }
 
 }  // namespace quic
diff --git a/quic/core/qpack/qpack_encoder.h b/quic/core/qpack/qpack_encoder.h
index 1f0e258..c170e09 100644
--- a/quic/core/qpack/qpack_encoder.h
+++ b/quic/core/qpack/qpack_encoder.h
@@ -16,6 +16,7 @@
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_instructions.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_exported_stats.h"
@@ -45,7 +46,8 @@
    public:
     virtual ~DecoderStreamErrorDelegate() {}
 
-    virtual void OnDecoderStreamError(absl::string_view error_message) = 0;
+    virtual void OnDecoderStreamError(QuicErrorCode error_code,
+                                      absl::string_view error_message) = 0;
   };
 
   QpackEncoder(DecoderStreamErrorDelegate* decoder_stream_error_delegate);
@@ -82,7 +84,8 @@
   void OnInsertCountIncrement(uint64_t increment) override;
   void OnHeaderAcknowledgement(QuicStreamId stream_id) override;
   void OnStreamCancellation(QuicStreamId stream_id) override;
-  void OnErrorDetected(absl::string_view error_message) override;
+  void OnErrorDetected(QuicErrorCode error_code,
+                       absl::string_view error_message) override;
 
   // delegate must be set if dynamic table capacity is not zero.
   void set_qpack_stream_sender_delegate(QpackStreamSenderDelegate* delegate) {
diff --git a/quic/core/qpack/qpack_encoder_stream_receiver.cc b/quic/core/qpack/qpack_encoder_stream_receiver.cc
index dedccc0..1cfa60b 100644
--- a/quic/core/qpack/qpack_encoder_stream_receiver.cc
+++ b/quic/core/qpack/qpack_encoder_stream_receiver.cc
@@ -51,11 +51,12 @@
   return true;
 }
 
-void QpackEncoderStreamReceiver::OnError(absl::string_view error_message) {
+void QpackEncoderStreamReceiver::OnInstructionDecodingError(
+    absl::string_view error_message) {
   DCHECK(!error_detected_);
 
   error_detected_ = true;
-  delegate_->OnErrorDetected(error_message);
+  delegate_->OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR, error_message);
 }
 
 }  // namespace quic
diff --git a/quic/core/qpack/qpack_encoder_stream_receiver.h b/quic/core/qpack/qpack_encoder_stream_receiver.h
index 5618b08..8b86547 100644
--- a/quic/core/qpack/qpack_encoder_stream_receiver.h
+++ b/quic/core/qpack/qpack_encoder_stream_receiver.h
@@ -11,6 +11,7 @@
 #include "absl/strings/string_view.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_stream_receiver.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -38,7 +39,8 @@
     // 5.2.4. Set Dynamic Table Capacity
     virtual void OnSetDynamicTableCapacity(uint64_t capacity) = 0;
     // Decoding error
-    virtual void OnErrorDetected(absl::string_view error_message) = 0;
+    virtual void OnErrorDetected(QuicErrorCode error_code,
+                                 absl::string_view error_message) = 0;
   };
 
   explicit QpackEncoderStreamReceiver(Delegate* delegate);
@@ -56,7 +58,7 @@
 
   // QpackInstructionDecoder::Delegate implementation.
   bool OnInstructionDecoded(const QpackInstruction* instruction) override;
-  void OnError(absl::string_view error_message) override;
+  void OnInstructionDecodingError(absl::string_view error_message) override;
 
  private:
   QpackInstructionDecoder instruction_decoder_;
diff --git a/quic/core/qpack/qpack_encoder_stream_receiver_test.cc b/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
index cb8e143..81e47e7 100644
--- a/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
+++ b/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
@@ -31,7 +31,7 @@
   MOCK_METHOD(void, OnSetDynamicTableCapacity, (uint64_t capacity), (override));
   MOCK_METHOD(void,
               OnErrorDetected,
-              (absl::string_view error_message),
+              (QuicErrorCode error_code, absl::string_view error_message),
               (override));
 };
 
@@ -71,13 +71,15 @@
 }
 
 TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceIndexTooLarge) {
-  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+  EXPECT_CALL(*delegate(), OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                           Eq("Encoded integer too large.")));
 
   Decode(quiche::QuicheTextUtils::HexDecode("bfffffffffffffffffffffff"));
 }
 
 TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceValueTooLong) {
-  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+  EXPECT_CALL(*delegate(), OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                           Eq("Encoded integer too large.")));
 
   Decode(quiche::QuicheTextUtils::HexDecode("c57fffffffffffffffffffff"));
 }
@@ -109,7 +111,8 @@
 // Name Length value is too large for varint decoder to decode.
 TEST_F(QpackEncoderStreamReceiverTest,
        InsertWithoutNameReferenceNameTooLongForVarintDecoder) {
-  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+  EXPECT_CALL(*delegate(), OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                           Eq("Encoded integer too large.")));
 
   Decode(quiche::QuicheTextUtils::HexDecode("5fffffffffffffffffffff"));
 }
@@ -117,7 +120,8 @@
 // Name Length value can be decoded by varint decoder but exceeds 1 MB limit.
 TEST_F(QpackEncoderStreamReceiverTest,
        InsertWithoutNameReferenceNameExceedsLimit) {
-  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("String literal too long.")));
+  EXPECT_CALL(*delegate(), OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                           Eq("String literal too long.")));
 
   Decode(quiche::QuicheTextUtils::HexDecode("5fffff7f"));
 }
@@ -125,7 +129,8 @@
 // Value Length value is too large for varint decoder to decode.
 TEST_F(QpackEncoderStreamReceiverTest,
        InsertWithoutNameReferenceValueTooLongForVarintDecoder) {
-  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+  EXPECT_CALL(*delegate(), OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                           Eq("Encoded integer too large.")));
 
   Decode(quiche::QuicheTextUtils::HexDecode("436261727fffffffffffffffffffff"));
 }
@@ -133,7 +138,8 @@
 // Value Length value can be decoded by varint decoder but exceeds 1 MB limit.
 TEST_F(QpackEncoderStreamReceiverTest,
        InsertWithoutNameReferenceValueExceedsLimit) {
-  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("String literal too long.")));
+  EXPECT_CALL(*delegate(), OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                           Eq("String literal too long.")));
 
   Decode(quiche::QuicheTextUtils::HexDecode("436261727fffff7f"));
 }
@@ -148,7 +154,8 @@
 }
 
 TEST_F(QpackEncoderStreamReceiverTest, DuplicateIndexTooLarge) {
-  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+  EXPECT_CALL(*delegate(), OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                           Eq("Encoded integer too large.")));
 
   Decode(quiche::QuicheTextUtils::HexDecode("1fffffffffffffffffffff"));
 }
@@ -163,11 +170,20 @@
 }
 
 TEST_F(QpackEncoderStreamReceiverTest, SetDynamicTableCapacityTooLarge) {
-  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+  EXPECT_CALL(*delegate(), OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                                           Eq("Encoded integer too large.")));
 
   Decode(quiche::QuicheTextUtils::HexDecode("3fffffffffffffffffffff"));
 }
 
+TEST_F(QpackEncoderStreamReceiverTest, InvalidHuffmanEncoding) {
+  EXPECT_CALL(*delegate(),
+              OnErrorDetected(QUIC_QPACK_ENCODER_STREAM_ERROR,
+                              Eq("Error in Huffman-encoded string.")));
+
+  Decode(quiche::QuicheTextUtils::HexDecode("c281ff"));
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/qpack/qpack_encoder_test.cc b/quic/core/qpack/qpack_encoder_test.cc
index 5cfa112..a248102 100644
--- a/quic/core/qpack/qpack_encoder_test.cc
+++ b/quic/core/qpack/qpack_encoder_test.cc
@@ -141,7 +141,8 @@
 
 TEST_F(QpackEncoderTest, DecoderStreamError) {
   EXPECT_CALL(decoder_stream_error_delegate_,
-              OnDecoderStreamError(Eq("Encoded integer too large.")));
+              OnDecoderStreamError(QUIC_QPACK_DECODER_STREAM_ERROR,
+                                   Eq("Encoded integer too large.")));
 
   QpackEncoder encoder(&decoder_stream_error_delegate_);
   encoder.set_qpack_stream_sender_delegate(&encoder_stream_sender_delegate_);
@@ -165,7 +166,8 @@
 TEST_F(QpackEncoderTest, ZeroInsertCountIncrement) {
   // Encoder receives insert count increment with forbidden value 0.
   EXPECT_CALL(decoder_stream_error_delegate_,
-              OnDecoderStreamError(Eq("Invalid increment value 0.")));
+              OnDecoderStreamError(QUIC_QPACK_DECODER_STREAM_ERROR,
+                                   Eq("Invalid increment value 0.")));
   encoder_.OnInsertCountIncrement(0);
 }
 
@@ -175,7 +177,8 @@
   // table insertions sent (zero).
   EXPECT_CALL(
       decoder_stream_error_delegate_,
-      OnDecoderStreamError(Eq("Increment value 1 raises known received count "
+      OnDecoderStreamError(QUIC_QPACK_DECODER_STREAM_ERROR,
+                           Eq("Increment value 1 raises known received count "
                               "to 1 exceeding inserted entry count 0")));
   encoder_.OnInsertCountIncrement(1);
 }
@@ -197,6 +200,7 @@
   // received count.  This must result in an error instead of a crash.
   EXPECT_CALL(decoder_stream_error_delegate_,
               OnDecoderStreamError(
+                  QUIC_QPACK_DECODER_STREAM_ERROR,
                   Eq("Insert Count Increment instruction causes overflow.")));
   encoder_.OnInsertCountIncrement(std::numeric_limits<uint64_t>::max());
 }
@@ -206,7 +210,8 @@
   // block with dynamic table entries was ever sent.
   EXPECT_CALL(
       decoder_stream_error_delegate_,
-      OnDecoderStreamError(Eq("Header Acknowledgement received for stream 0 "
+      OnDecoderStreamError(QUIC_QPACK_DECODER_STREAM_ERROR,
+                           Eq("Header Acknowledgement received for stream 0 "
                               "with no outstanding header blocks.")));
   encoder_.OnHeaderAcknowledgement(/* stream_id = */ 0);
 }
diff --git a/quic/core/qpack/qpack_instruction_decoder.cc b/quic/core/qpack/qpack_instruction_decoder.cc
index 47b939d..b4c6dd3 100644
--- a/quic/core/qpack/qpack_instruction_decoder.cc
+++ b/quic/core/qpack/qpack_instruction_decoder.cc
@@ -326,7 +326,7 @@
   DCHECK(!error_detected_);
 
   error_detected_ = true;
-  delegate_->OnError(error_message);
+  delegate_->OnInstructionDecodingError(error_message);
 }
 
 }  // namespace quic
diff --git a/quic/core/qpack/qpack_instruction_decoder.h b/quic/core/qpack/qpack_instruction_decoder.h
index c6df1f3..759c8f0 100644
--- a/quic/core/qpack/qpack_instruction_decoder.h
+++ b/quic/core/qpack/qpack_instruction_decoder.h
@@ -42,7 +42,8 @@
     // No more data is processed afterwards.
     // Implementations are allowed to destroy the QpackInstructionDecoder
     // instance synchronously.
-    virtual void OnError(absl::string_view error_message) = 0;
+    virtual void OnInstructionDecodingError(
+        absl::string_view error_message) = 0;
   };
 
   // Both |*language| and |*delegate| must outlive this object.
@@ -53,7 +54,8 @@
 
   // Provide a data fragment to decode.  Must not be called after an error has
   // occurred.  Must not be called with empty |data|.  Return true on success,
-  // false on error (in which case Delegate::OnError() is called synchronously).
+  // false on error (in which case Delegate::OnInstructionDecodingError() is
+  // called synchronously).
   bool Decode(absl::string_view data);
 
   // Returns true if no decoding has taken place yet or if the last instruction
@@ -107,7 +109,7 @@
   // Returns a pointer to an element of |*language_|.
   const QpackInstruction* LookupOpcode(uint8_t byte) const;
 
-  // Stops decoding and calls Delegate::OnError().
+  // Stops decoding and calls Delegate::OnInstructionDecodingError().
   void OnError(absl::string_view error_message);
 
   // Describes the language used for decoding.
diff --git a/quic/core/qpack/qpack_instruction_decoder_test.cc b/quic/core/qpack/qpack_instruction_decoder_test.cc
index 2674f92..25c7f7a 100644
--- a/quic/core/qpack/qpack_instruction_decoder_test.cc
+++ b/quic/core/qpack/qpack_instruction_decoder_test.cc
@@ -16,7 +16,7 @@
 using ::testing::_;
 using ::testing::Eq;
 using ::testing::Expectation;
-using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
 using ::testing::Return;
 using ::testing::StrictMock;
 using ::testing::Values;
@@ -65,7 +65,10 @@
               OnInstructionDecoded,
               (const QpackInstruction*),
               (override));
-  MOCK_METHOD(void, OnError, (absl::string_view error_message), (override));
+  MOCK_METHOD(void,
+              OnInstructionDecodingError,
+              (absl::string_view error_message),
+              (override));
 };
 
 class QpackInstructionDecoderTest : public QuicTestWithParam<FragmentMode> {
@@ -79,10 +82,8 @@
   void SetUp() override {
     // Destroy QpackInstructionDecoder on error to test that it does not crash.
     // See https://crbug.com/1025209.
-    ON_CALL(delegate_, OnError(_))
-        .WillByDefault(Invoke([this](absl::string_view /* error_message */) {
-          decoder_.reset();
-        }));
+    ON_CALL(delegate_, OnInstructionDecodingError(_))
+        .WillByDefault(InvokeWithoutArgs([this]() { decoder_.reset(); }));
   }
 
   // Decode one full instruction with fragment sizes dictated by
@@ -163,34 +164,40 @@
 }
 
 TEST_P(QpackInstructionDecoderTest, InvalidHuffmanEncoding) {
-  EXPECT_CALL(delegate_, OnError(Eq("Error in Huffman-encoded string.")));
+  EXPECT_CALL(delegate_, OnInstructionDecodingError(
+                             Eq("Error in Huffman-encoded string.")));
   DecodeInstruction(quiche::QuicheTextUtils::HexDecode("c1ff"));
 }
 
 TEST_P(QpackInstructionDecoderTest, InvalidVarintEncoding) {
-  EXPECT_CALL(delegate_, OnError(Eq("Encoded integer too large.")));
+  EXPECT_CALL(delegate_,
+              OnInstructionDecodingError(Eq("Encoded integer too large.")));
   DecodeInstruction(
       quiche::QuicheTextUtils::HexDecode("ffffffffffffffffffffff"));
 }
 
+TEST_P(QpackInstructionDecoderTest, StringLiteralTooLong) {
+  EXPECT_CALL(delegate_,
+              OnInstructionDecodingError(Eq("String literal too long.")));
+  DecodeInstruction(quiche::QuicheTextUtils::HexDecode("bfffff7f"));
+}
+
 TEST_P(QpackInstructionDecoderTest, DelegateSignalsError) {
   // First instruction is valid.
   Expectation first_call =
       EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
-          .WillOnce(Invoke(
-              [this](const QpackInstruction * /* instruction */) -> bool {
-                EXPECT_EQ(1u, decoder_->varint());
-                return true;
-              }));
+          .WillOnce(InvokeWithoutArgs([this]() -> bool {
+            EXPECT_EQ(1u, decoder_->varint());
+            return true;
+          }));
 
   // Second instruction is invalid.  Decoding must halt.
   EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
       .After(first_call)
-      .WillOnce(
-          Invoke([this](const QpackInstruction * /* instruction */) -> bool {
-            EXPECT_EQ(2u, decoder_->varint());
-            return false;
-          }));
+      .WillOnce(InvokeWithoutArgs([this]() -> bool {
+        EXPECT_EQ(2u, decoder_->varint());
+        return false;
+      }));
 
   EXPECT_FALSE(decoder_->Decode(
       quiche::QuicheTextUtils::HexDecode("01000200030004000500")));
@@ -200,12 +207,11 @@
 // Delegate::OnInstructionDecoded() call as long as it returns false.
 TEST_P(QpackInstructionDecoderTest, DelegateSignalsErrorAndDestroysDecoder) {
   EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
-      .WillOnce(
-          Invoke([this](const QpackInstruction * /* instruction */) -> bool {
-            EXPECT_EQ(1u, decoder_->varint());
-            decoder_.reset();
-            return false;
-          }));
+      .WillOnce(InvokeWithoutArgs([this]() -> bool {
+        EXPECT_EQ(1u, decoder_->varint());
+        decoder_.reset();
+        return false;
+      }));
   DecodeInstruction(quiche::QuicheTextUtils::HexDecode("0100"));
 }
 
diff --git a/quic/core/qpack/qpack_progressive_decoder.cc b/quic/core/qpack/qpack_progressive_decoder.cc
index d681eb6..2b3c21e 100644
--- a/quic/core/qpack/qpack_progressive_decoder.cc
+++ b/quic/core/qpack/qpack_progressive_decoder.cc
@@ -89,6 +89,14 @@
   }
 }
 
+void QpackProgressiveDecoder::OnError(absl::string_view error_message) {
+  DCHECK(!error_detected_);
+
+  error_detected_ = true;
+  // Might destroy |this|.
+  handler_->OnDecodingErrorDetected(error_message);
+}
+
 bool QpackProgressiveDecoder::OnInstructionDecoded(
     const QpackInstruction* instruction) {
   if (instruction == QpackPrefixInstruction()) {
@@ -114,12 +122,9 @@
   return DoLiteralHeaderFieldInstruction();
 }
 
-void QpackProgressiveDecoder::OnError(absl::string_view error_message) {
-  DCHECK(!error_detected_);
-
-  error_detected_ = true;
-  // Might destroy |this|.
-  handler_->OnDecodingErrorDetected(error_message);
+void QpackProgressiveDecoder::OnInstructionDecodingError(
+    absl::string_view error_message) {
+  OnError(error_message);
 }
 
 void QpackProgressiveDecoder::OnInsertCountReachedThreshold() {
diff --git a/quic/core/qpack/qpack_progressive_decoder.h b/quic/core/qpack/qpack_progressive_decoder.h
index 723aa7f..ecac020 100644
--- a/quic/core/qpack/qpack_progressive_decoder.h
+++ b/quic/core/qpack/qpack_progressive_decoder.h
@@ -95,9 +95,12 @@
   // through Decode().  No methods must be called afterwards.
   void EndHeaderBlock();
 
+  // Called on error.
+  void OnError(absl::string_view error_message);
+
   // QpackInstructionDecoder::Delegate implementation.
   bool OnInstructionDecoded(const QpackInstruction* instruction) override;
-  void OnError(absl::string_view error_message) override;
+  void OnInstructionDecodingError(absl::string_view error_message) override;
 
   // QpackHeaderTable::Observer implementation.
   void OnInsertCountReachedThreshold() override;
diff --git a/quic/test_tools/qpack/qpack_decoder_test_utils.cc b/quic/test_tools/qpack/qpack_decoder_test_utils.cc
index 3d4f8ca..d7de01f 100644
--- a/quic/test_tools/qpack/qpack_decoder_test_utils.cc
+++ b/quic/test_tools/qpack/qpack_decoder_test_utils.cc
@@ -15,6 +15,7 @@
 namespace test {
 
 void NoopEncoderStreamErrorDelegate::OnEncoderStreamError(
+    QuicErrorCode /* error_code */,
     absl::string_view /*error_message*/) {}
 
 TestHeadersHandler::TestHeadersHandler()
diff --git a/quic/test_tools/qpack/qpack_decoder_test_utils.h b/quic/test_tools/qpack/qpack_decoder_test_utils.h
index b7a9410..8b1b4e1 100644
--- a/quic/test_tools/qpack/qpack_decoder_test_utils.h
+++ b/quic/test_tools/qpack/qpack_decoder_test_utils.h
@@ -23,7 +23,8 @@
  public:
   ~NoopEncoderStreamErrorDelegate() override = default;
 
-  void OnEncoderStreamError(absl::string_view error_message) override;
+  void OnEncoderStreamError(QuicErrorCode error_code,
+                            absl::string_view error_message) override;
 };
 
 // Mock QpackDecoder::EncoderStreamErrorDelegate implementation.
@@ -34,7 +35,7 @@
 
   MOCK_METHOD(void,
               OnEncoderStreamError,
-              (absl::string_view error_message),
+              (QuicErrorCode error_code, absl::string_view error_message),
               (override));
 };
 
diff --git a/quic/test_tools/qpack/qpack_encoder_test_utils.cc b/quic/test_tools/qpack/qpack_encoder_test_utils.cc
index 3f05815..9c8fd92 100644
--- a/quic/test_tools/qpack/qpack_encoder_test_utils.cc
+++ b/quic/test_tools/qpack/qpack_encoder_test_utils.cc
@@ -11,6 +11,7 @@
 namespace test {
 
 void NoopDecoderStreamErrorDelegate::OnDecoderStreamError(
+    QuicErrorCode /*error_code*/,
     absl::string_view /*error_message*/) {}
 
 }  // namespace test
diff --git a/quic/test_tools/qpack/qpack_encoder_test_utils.h b/quic/test_tools/qpack/qpack_encoder_test_utils.h
index c6dcd72..9d4e23e 100644
--- a/quic/test_tools/qpack/qpack_encoder_test_utils.h
+++ b/quic/test_tools/qpack/qpack_encoder_test_utils.h
@@ -22,7 +22,8 @@
  public:
   ~NoopDecoderStreamErrorDelegate() override = default;
 
-  void OnDecoderStreamError(absl::string_view error_message) override;
+  void OnDecoderStreamError(QuicErrorCode error_code,
+                            absl::string_view error_message) override;
 };
 
 // Mock QpackEncoder::DecoderStreamErrorDelegate implementation.
@@ -33,7 +34,7 @@
 
   MOCK_METHOD(void,
               OnDecoderStreamError,
-              (absl::string_view error_message),
+              (QuicErrorCode error_code, absl::string_view error_message),
               (override));
 };
 
diff --git a/quic/test_tools/qpack/qpack_offline_decoder.cc b/quic/test_tools/qpack/qpack_offline_decoder.cc
index 35f8622..74c6fa1 100644
--- a/quic/test_tools/qpack/qpack_offline_decoder.cc
+++ b/quic/test_tools/qpack/qpack_offline_decoder.cc
@@ -67,8 +67,10 @@
 }
 
 void QpackOfflineDecoder::OnEncoderStreamError(
+    QuicErrorCode error_code,
     absl::string_view error_message) {
-  QUIC_LOG(ERROR) << "Encoder stream error: " << error_message;
+  QUIC_LOG(ERROR) << "Encoder stream error: "
+                  << QuicErrorCodeToString(error_code) << " " << error_message;
   encoder_stream_error_detected_ = true;
 }
 
diff --git a/quic/test_tools/qpack/qpack_offline_decoder.h b/quic/test_tools/qpack/qpack_offline_decoder.h
index 3393650..47b93fc 100644
--- a/quic/test_tools/qpack/qpack_offline_decoder.h
+++ b/quic/test_tools/qpack/qpack_offline_decoder.h
@@ -9,6 +9,7 @@
 
 #include "absl/strings/string_view.h"
 #include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_decoder_test_utils.h"
 #include "net/third_party/quiche/src/quic/test_tools/qpack/qpack_test_utils.h"
 #include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
@@ -33,7 +34,8 @@
                                   absl::string_view expected_headers_filename);
 
   // QpackDecoder::EncoderStreamErrorDelegate implementation:
-  void OnEncoderStreamError(absl::string_view error_message) override;
+  void OnEncoderStreamError(QuicErrorCode error_code,
+                            absl::string_view error_message) override;
 
  private:
   // Data structure to hold TestHeadersHandler and QpackProgressiveDecoder until