Error handling cleanup
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index 3711e0c..6a2a54a 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -1252,11 +1252,16 @@
 }
 
 static void send_goaway(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
-                        grpc_chttp2_error_code error, grpc_slice data) {
+                        grpc_error *error) {
   t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED;
-  grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)error, data,
-                            &t->qbuf);
+  grpc_http2_error_code http_error;
+  const char *msg;
+  grpc_error_get_status(error, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL, &msg,
+                        &http_error);
+  grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)http_error,
+                            grpc_slice_from_copied_string(msg), &t->qbuf);
   grpc_chttp2_initiate_write(exec_ctx, t, false, "goaway_sent");
+  GRPC_ERROR_UNREF(error);
 }
 
 static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx,
@@ -1272,10 +1277,8 @@
         op->on_connectivity_state_change);
   }
 
-  if (op->send_goaway) {
-    send_goaway(exec_ctx, t,
-                grpc_chttp2_grpc_status_to_http2_error(op->goaway_status),
-                grpc_slice_ref_internal(*op->goaway_message));
+  if (op->goaway_error) {
+    send_goaway(exec_ctx, t, op->goaway_error);
   }
 
   if (op->set_accept_stream) {
@@ -1428,33 +1431,6 @@
   maybe_start_some_streams(exec_ctx, t);
 }
 
-static void status_codes_from_error(grpc_error *error, gpr_timespec deadline,
-                                    grpc_chttp2_error_code *http2_error,
-                                    grpc_status_code *grpc_status) {
-  intptr_t ip_http;
-  intptr_t ip_grpc;
-  bool have_http =
-      grpc_error_get_int(error, GRPC_ERROR_INT_HTTP2_ERROR, &ip_http);
-  bool have_grpc =
-      grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &ip_grpc);
-  if (have_http) {
-    *http2_error = (grpc_chttp2_error_code)ip_http;
-  } else if (have_grpc) {
-    *http2_error =
-        grpc_chttp2_grpc_status_to_http2_error((grpc_status_code)ip_grpc);
-  } else {
-    *http2_error = GRPC_CHTTP2_INTERNAL_ERROR;
-  }
-  if (have_grpc) {
-    *grpc_status = (grpc_status_code)ip_grpc;
-  } else if (have_http) {
-    *grpc_status = grpc_chttp2_http2_error_to_grpc_status(
-        (grpc_chttp2_error_code)ip_http, deadline);
-  } else {
-    *grpc_status = GRPC_STATUS_INTERNAL;
-  }
-}
-
 void grpc_chttp2_cancel_stream(grpc_exec_ctx *exec_ctx,
                                grpc_chttp2_transport *t, grpc_chttp2_stream *s,
                                grpc_error *due_to_error) {
@@ -1465,28 +1441,14 @@
   }
 
   if (!s->read_closed || !s->write_closed) {
-    grpc_status_code grpc_status;
-    grpc_chttp2_error_code http_error;
-    status_codes_from_error(due_to_error, s->deadline, &http_error,
-                            &grpc_status);
-
     if (s->id != 0) {
+      grpc_http2_error_code http_error;
+      grpc_error_get_status(due_to_error, s->deadline, NULL, NULL, &http_error);
       grpc_slice_buffer_add(
           &t->qbuf, grpc_chttp2_rst_stream_create(s->id, (uint32_t)http_error,
                                                   &s->stats.outgoing));
       grpc_chttp2_initiate_write(exec_ctx, t, false, "rst_stream");
     }
-
-    const char *msg =
-        grpc_error_get_str(due_to_error, GRPC_ERROR_STR_GRPC_MESSAGE);
-    bool free_msg = false;
-    if (msg == NULL) {
-      free_msg = true;
-      msg = grpc_error_string(due_to_error);
-    }
-    grpc_slice msg_slice = grpc_slice_from_copied_string(msg);
-    grpc_chttp2_fake_status(exec_ctx, t, s, grpc_status, &msg_slice);
-    if (free_msg) grpc_error_free_string(msg);
   }
   if (due_to_error != GRPC_ERROR_NONE && !s->seen_error) {
     s->seen_error = true;
@@ -1496,8 +1458,11 @@
 }
 
 void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
-                             grpc_chttp2_stream *s, grpc_status_code status,
-                             grpc_slice *slice) {
+                             grpc_chttp2_stream *s, grpc_error *error) {
+  grpc_status_code status;
+  const char *msg;
+  grpc_error_get_status(error, s->deadline, &status, &msg, NULL);
+
   if (status != GRPC_STATUS_OK) {
     s->seen_error = true;
   }
@@ -1515,18 +1480,17 @@
         &s->metadata_buffer[1],
         grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_STATUS,
                                 grpc_slice_from_copied_string(status_string)));
-    if (slice) {
+    if (msg != NULL) {
       grpc_chttp2_incoming_metadata_buffer_add(
           &s->metadata_buffer[1],
           grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_MESSAGE,
-                                  grpc_slice_ref_internal(*slice)));
+                                  grpc_slice_from_copied_string(msg)));
     }
     s->published_metadata[1] = GRPC_METADATA_SYNTHESIZED_FROM_FAKE;
     grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s);
   }
-  if (slice) {
-    grpc_slice_unref_internal(exec_ctx, *slice);
-  }
+
+  GRPC_ERROR_UNREF(error);
 }
 
 static void add_error(grpc_error *error, grpc_error **refs, size_t *nrefs) {
@@ -1596,6 +1560,7 @@
     return;
   }
   if (close_reads && !s->read_closed) {
+    grpc_chttp2_fake_status(exec_ctx, t, s, GRPC_ERROR_REF(error));
     s->read_closed_error = GRPC_ERROR_REF(error);
     s->read_closed = true;
     for (int i = 0; i < 2; i++) {
@@ -1636,7 +1601,7 @@
   uint32_t len = 0;
   grpc_status_code grpc_status;
   const char *msg;
-  grpc_error_get_status(error, &grpc_status, &msg);
+  grpc_error_get_status(error, s->deadline, &grpc_status, &msg, NULL);
 
   GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100);
 
@@ -1719,14 +1684,9 @@
     grpc_slice_buffer_add(&t->qbuf, grpc_slice_from_copied_string(msg));
   }
   grpc_slice_buffer_add(
-      &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_CHTTP2_NO_ERROR,
+      &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR,
                                               &s->stats.outgoing));
 
-  grpc_slice msg_slice =
-      msg == NULL ? grpc_empty_slice() : grpc_slice_from_copied_string(msg);
-  grpc_chttp2_fake_status(exec_ctx, t, s, grpc_status,
-                          msg == NULL ? NULL : &msg_slice);
-
   grpc_chttp2_mark_stream_closed(exec_ctx, t, s, 1, 1, error);
   grpc_chttp2_initiate_write(exec_ctx, t, false, "close_from_api");
 }
@@ -2173,8 +2133,10 @@
       gpr_log(GPR_DEBUG, "HTTP2: %s - send goaway to free memory",
               t->peer_string);
     }
-    send_goaway(exec_ctx, t, GRPC_CHTTP2_ENHANCE_YOUR_CALM,
-                grpc_slice_from_static_string("Buffers full"));
+    send_goaway(exec_ctx, t,
+                grpc_error_set_int(GRPC_ERROR_CREATE("Buffers full"),
+                                   GRPC_ERROR_INT_HTTP2_ERROR,
+                                   GRPC_HTTP2_ENHANCE_YOUR_CALM));
   } else if (error == GRPC_ERROR_NONE && grpc_resource_quota_trace) {
     gpr_log(GPR_DEBUG,
             "HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR
@@ -2203,7 +2165,7 @@
     grpc_chttp2_cancel_stream(
         exec_ctx, t, s, grpc_error_set_int(GRPC_ERROR_CREATE("Buffers full"),
                                            GRPC_ERROR_INT_HTTP2_ERROR,
-                                           GRPC_CHTTP2_ENHANCE_YOUR_CALM));
+                                           GRPC_HTTP2_ENHANCE_YOUR_CALM));
     if (n > 1) {
       /* Since we cancel one stream per destructive reclamation, if
          there are more streams left, we can immediately post a new
diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c b/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
index b4c5ed7..583ac9e 100644
--- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
+++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
@@ -39,8 +39,7 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/transport/chttp2/transport/frame.h"
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
-#include "src/core/ext/transport/chttp2/transport/status_conversion.h"
+#include "src/core/lib/transport/http2_errors.h"
 
 grpc_slice grpc_chttp2_rst_stream_create(uint32_t id, uint32_t code,
                                          grpc_transport_one_way_stats *stats) {
@@ -109,17 +108,9 @@
                       (((uint32_t)p->reason_bytes[2]) << 8) |
                       (((uint32_t)p->reason_bytes[3]));
     grpc_error *error = GRPC_ERROR_NONE;
-    if (reason != GRPC_CHTTP2_NO_ERROR) {
+    if (reason != GRPC_HTTP2_NO_ERROR) {
       error = grpc_error_set_int(GRPC_ERROR_CREATE("RST_STREAM"),
                                  GRPC_ERROR_INT_HTTP2_ERROR, (intptr_t)reason);
-      grpc_status_code status_code = grpc_chttp2_http2_error_to_grpc_status(
-          (grpc_chttp2_error_code)reason, s->deadline);
-      char *status_details;
-      gpr_asprintf(&status_details, "Received RST_STREAM with error code %d",
-                   reason);
-      grpc_slice slice_details = grpc_slice_from_copied_string(status_details);
-      gpr_free(status_details);
-      grpc_chttp2_fake_status(exec_ctx, t, s, status_code, &slice_details);
     }
     grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, true, error);
   }
diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.c b/src/core/ext/transport/chttp2/transport/frame_settings.c
index bfed41f..be9b663 100644
--- a/src/core/ext/transport/chttp2/transport/frame_settings.c
+++ b/src/core/ext/transport/chttp2/transport/frame_settings.c
@@ -52,21 +52,21 @@
 const grpc_chttp2_setting_parameters
     grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = {
         {NULL, 0, 0, 0, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,
-         GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_HTTP2_PROTOCOL_ERROR},
         {"HEADER_TABLE_SIZE", 4096, 0, 0xffffffff,
-         GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
         {"ENABLE_PUSH", 1, 0, 1, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,
-         GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_HTTP2_PROTOCOL_ERROR},
         {"MAX_CONCURRENT_STREAMS", 0xffffffffu, 0, 0xffffffffu,
-         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
         {"INITIAL_WINDOW_SIZE", 65535, 0, 0x7fffffffu,
          GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE,
-         GRPC_CHTTP2_FLOW_CONTROL_ERROR},
+         GRPC_HTTP2_FLOW_CONTROL_ERROR},
         {"MAX_FRAME_SIZE", 16384, 16384, 16777215,
-         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},
         {"MAX_HEADER_LIST_SIZE", MAX_MAX_HEADER_LIST_SIZE, 0,
          MAX_MAX_HEADER_LIST_SIZE, GRPC_CHTTP2_CLAMP_INVALID_VALUE,
-         GRPC_CHTTP2_PROTOCOL_ERROR},
+         GRPC_HTTP2_PROTOCOL_ERROR},
 };
 
 static uint8_t *fill_header(uint8_t *out, uint32_t length, uint8_t flags) {
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.c b/src/core/ext/transport/chttp2/transport/hpack_parser.c
index 6d6d0de..40f5120 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.c
@@ -1644,7 +1644,7 @@
   grpc_chttp2_transport *t = s->t;
   if (!s->write_closed) {
     grpc_slice_buffer_add(
-        &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_CHTTP2_NO_ERROR,
+        &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR,
                                                 &s->stats.outgoing));
     grpc_chttp2_initiate_write(exec_ctx, t, false, "force_rst_stream");
     grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, true, GRPC_ERROR_NONE);
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index a52acba..43fd10f 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -612,8 +612,7 @@
                                uint32_t stream_id, int64_t val1, int64_t val2);
 
 void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
-                             grpc_chttp2_stream *stream,
-                             grpc_status_code status, grpc_slice *details);
+                             grpc_chttp2_stream *stream, grpc_error *error);
 void grpc_chttp2_mark_stream_closed(grpc_exec_ctx *exec_ctx,
                                     grpc_chttp2_transport *t,
                                     grpc_chttp2_stream *s, int close_reads,
diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c
index c5a99e5..11e4655 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.c
+++ b/src/core/ext/transport/chttp2/transport/parsing.c
@@ -39,11 +39,11 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
-#include "src/core/ext/transport/chttp2/transport/status_conversion.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_string_helpers.h"
+#include "src/core/lib/transport/http2_errors.h"
 #include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/status_conversion.h"
 #include "src/core/lib/transport/timeout_encoding.h"
 
 static grpc_error *init_frame_parser(grpc_exec_ctx *exec_ctx,
@@ -433,7 +433,7 @@
     }
     grpc_slice_buffer_add(
         &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id,
-                                                GRPC_CHTTP2_PROTOCOL_ERROR,
+                                                GRPC_HTTP2_PROTOCOL_ERROR,
                                                 &s->stats.outgoing));
     return init_skip_frame_parser(exec_ctx, t, 0);
   } else {
@@ -758,7 +758,7 @@
       s->forced_close_error = err;
       grpc_slice_buffer_add(
           &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id,
-                                                  GRPC_CHTTP2_PROTOCOL_ERROR,
+                                                  GRPC_HTTP2_PROTOCOL_ERROR,
                                                   &s->stats.outgoing));
     } else {
       GRPC_ERROR_UNREF(err);
diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c
index ef2010a..182aff1 100644
--- a/src/core/ext/transport/chttp2/transport/writing.c
+++ b/src/core/ext/transport/chttp2/transport/writing.c
@@ -37,9 +37,9 @@
 
 #include <grpc/support/log.h>
 
-#include "src/core/ext/transport/chttp2/transport/http2_errors.h"
 #include "src/core/lib/profiling/timers.h"
 #include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/transport/http2_errors.h"
 
 static void add_to_write_list(grpc_chttp2_write_cb **list,
                               grpc_chttp2_write_cb *cb) {
@@ -164,7 +164,7 @@
             s->sent_trailing_metadata = true;
             if (!t->is_client && !s->read_closed) {
               grpc_slice_buffer_add(&t->outbuf, grpc_chttp2_rst_stream_create(
-                                                    s->id, GRPC_CHTTP2_NO_ERROR,
+                                                    s->id, GRPC_HTTP2_NO_ERROR,
                                                     &s->stats.outgoing));
             }
           }
@@ -197,7 +197,7 @@
         if (!t->is_client && !s->read_closed) {
           grpc_slice_buffer_add(
               &t->outbuf, grpc_chttp2_rst_stream_create(
-                              s->id, GRPC_CHTTP2_NO_ERROR, &s->stats.outgoing));
+                              s->id, GRPC_HTTP2_NO_ERROR, &s->stats.outgoing));
         }
         now_writing = true;
       }
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index 4abf981..17fda43 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -336,10 +336,7 @@
       args->server_transport_data, path, call->start_time, send_deadline,
       CALL_STACK_FROM_CALL(call));
   if (error != GRPC_ERROR_NONE) {
-    grpc_status_code status;
-    const char *error_str;
-    grpc_error_get_status(error, &status, &error_str);
-    cancel_with_status(exec_ctx, call, status, error_str);
+    cancel_with_error(exec_ctx, call, GRPC_ERROR_REF(error));
   }
   if (args->cq != NULL) {
     GPR_ASSERT(
@@ -612,7 +609,8 @@
 
       grpc_status_code code;
       const char *msg = NULL;
-      grpc_error_get_status(call->status[i].error, &code, &msg);
+      grpc_error_get_status(call->status[i].error, call->send_deadline, &code,
+                            &msg, NULL);
       set_value(code, set_value_user_data);
       if (details != NULL) {
         *details = grpc_slice_from_copied_string(msg);
diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
index 7af951f..b5f905b 100644
--- a/src/core/lib/surface/server.c
+++ b/src/core/lib/surface/server.c
@@ -280,18 +280,20 @@
 }
 
 static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel,
-                          int send_goaway, grpc_error *send_disconnect) {
+                          bool send_goaway, grpc_error *send_disconnect) {
   struct shutdown_cleanup_args *sc = gpr_malloc(sizeof(*sc));
   grpc_closure_init(&sc->closure, shutdown_cleanup, sc,
                     grpc_schedule_on_exec_ctx);
   grpc_transport_op *op = grpc_make_transport_op(&sc->closure);
   grpc_channel_element *elem;
 
-  op->send_goaway = send_goaway;
+  op->goaway_error =
+      send_goaway
+          ? grpc_error_set_int(GRPC_ERROR_CREATE("Server shutdown"),
+                               GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK)
+          : GRPC_ERROR_NONE;
   op->set_accept_stream = true;
   sc->slice = grpc_slice_from_copied_string("Server shutdown");
-  op->goaway_message = &sc->slice;
-  op->goaway_status = GRPC_STATUS_OK;
   op->disconnect_with_error = send_disconnect;
 
   elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0);
diff --git a/src/core/lib/transport/error_utils.c b/src/core/lib/transport/error_utils.c
index 6403cf8..da77828 100644
--- a/src/core/lib/transport/error_utils.c
+++ b/src/core/lib/transport/error_utils.c
@@ -32,12 +32,14 @@
  */
 
 #include "src/core/lib/transport/error_utils.h"
-#include "src/core/lib/iomgr/error_internal.h"
 
-static grpc_error *recursively_find_error_with_status(grpc_error *error,
-                                                      intptr_t *status) {
+#include "src/core/lib/iomgr/error_internal.h"
+#include "src/core/lib/transport/status_conversion.h"
+
+static grpc_error *recursively_find_error_with_field(grpc_error *error,
+                                                     grpc_error_ints which) {
   // If the error itself has a status code, return it.
-  if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, status)) {
+  if (grpc_error_get_int(error, which, NULL)) {
     return error;
   }
   if (grpc_error_is_special(error)) return NULL;
@@ -46,32 +48,64 @@
   while (true) {
     grpc_error *child_error = gpr_avl_get(error->errs, (void *)key++);
     if (child_error == NULL) break;
-    grpc_error *result =
-        recursively_find_error_with_status(child_error, status);
+    grpc_error *result = recursively_find_error_with_field(child_error, which);
     if (result != NULL) return result;
   }
   return NULL;
 }
 
-void grpc_error_get_status(grpc_error *error, grpc_status_code *code,
-                           const char **msg) {
-  // Populate code.
+void grpc_error_get_status(grpc_error *error, gpr_timespec deadline,
+                           grpc_status_code *code, const char **msg,
+                           grpc_http2_error_code *http_error) {
   // Start with the parent error and recurse through the tree of children
   // until we find the first one that has a status code.
-  intptr_t status = GRPC_STATUS_UNKNOWN;  // Default in case we don't find one.
-  grpc_error *found_error = recursively_find_error_with_status(error, &status);
-  *code = (grpc_status_code)status;
-  // Now populate msg.
+  grpc_error *found_error =
+      recursively_find_error_with_field(error, GRPC_ERROR_INT_GRPC_STATUS);
+  if (found_error == NULL) {
+    /// If no grpc-status exists, retry through the tree to find a http2 error
+    /// code
+    found_error =
+        recursively_find_error_with_field(error, GRPC_ERROR_INT_HTTP2_ERROR);
+  }
+
   // If we found an error with a status code above, use that; otherwise,
   // fall back to using the parent error.
   if (found_error == NULL) found_error = error;
+
+  grpc_status_code status = GRPC_STATUS_UNKNOWN;
+  intptr_t integer;
+  if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, &integer)) {
+    status = (grpc_status_code)integer;
+  } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR,
+                                &integer)) {
+    status = grpc_http2_error_to_grpc_status((grpc_http2_error_code)integer,
+                                             deadline);
+  }
+  if (code != NULL) *code = status;
+
+  if (http_error != NULL) {
+    if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) {
+      *http_error = (grpc_http2_error_code)integer;
+    } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS,
+                                  &integer)) {
+      *http_error = grpc_status_to_http2_error((grpc_status_code)integer);
+    } else {
+      *http_error = found_error == GRPC_ERROR_NONE ? GRPC_HTTP2_NO_ERROR
+                                                   : GRPC_HTTP2_INTERNAL_ERROR;
+    }
+  }
+
   // If the error has a status message, use it.  Otherwise, fall back to
   // the error description.
-  *msg = grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE);
-  if (*msg == NULL && status != GRPC_STATUS_OK) {
-    *msg = grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION);
-    if (*msg == NULL) *msg = "unknown error";  // Just in case.
+  if (msg != NULL) {
+    *msg = grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE);
+    if (*msg == NULL && error != GRPC_ERROR_NONE) {
+      *msg = grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION);
+      if (*msg == NULL) *msg = "unknown error";  // Just in case.
+    }
   }
+
+  if (found_error == NULL) found_error = error;
 }
 
 bool grpc_error_has_clear_grpc_status(grpc_error *error) {
diff --git a/src/core/lib/transport/error_utils.h b/src/core/lib/transport/error_utils.h
index 541dba8..f72c3dc 100644
--- a/src/core/lib/transport/error_utils.h
+++ b/src/core/lib/transport/error_utils.h
@@ -35,12 +35,18 @@
 #define GRPC_ERROR_UTILS_H
 
 #include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/transport/http2_errors.h"
 
 /// A utility function to get the status code and message to be returned
 /// to the application.  If not set in the top-level message, looks
 /// through child errors until it finds the first one with these attributes.
-void grpc_error_get_status(grpc_error *error, grpc_status_code *code,
-                           const char **msg);
+/// All attributes are pulled from the same child error. If any of the
+/// attributes (code, msg, http_status) are unneeded, they can be passed as
+/// NULL.
+void grpc_error_get_status(grpc_error *error, gpr_timespec deadline,
+                           grpc_status_code *code, const char **msg,
+                           grpc_http2_error_code *http_status);
+
 /// A utility function to check whether there is a clear status code that
 /// doesn't need to be guessed in \a error. This means that \a error or some
 /// child has GRPC_ERROR_INT_GRPC_STATUS set, or that it is GRPC_ERROR_NONE or
diff --git a/src/core/lib/transport/http2_errors.h b/src/core/lib/transport/http2_errors.h
index deab2b7..bf24438 100644
--- a/src/core/lib/transport/http2_errors.h
+++ b/src/core/lib/transport/http2_errors.h
@@ -36,21 +36,21 @@
 
 /* error codes for RST_STREAM from http2 draft 14 section 7 */
 typedef enum {
-  GRPC_CHTTP2_NO_ERROR = 0x0,
-  GRPC_CHTTP2_PROTOCOL_ERROR = 0x1,
-  GRPC_CHTTP2_INTERNAL_ERROR = 0x2,
-  GRPC_CHTTP2_FLOW_CONTROL_ERROR = 0x3,
-  GRPC_CHTTP2_SETTINGS_TIMEOUT = 0x4,
-  GRPC_CHTTP2_STREAM_CLOSED = 0x5,
-  GRPC_CHTTP2_FRAME_SIZE_ERROR = 0x6,
-  GRPC_CHTTP2_REFUSED_STREAM = 0x7,
-  GRPC_CHTTP2_CANCEL = 0x8,
-  GRPC_CHTTP2_COMPRESSION_ERROR = 0x9,
-  GRPC_CHTTP2_CONNECT_ERROR = 0xa,
-  GRPC_CHTTP2_ENHANCE_YOUR_CALM = 0xb,
-  GRPC_CHTTP2_INADEQUATE_SECURITY = 0xc,
+  GRPC_HTTP2_NO_ERROR = 0x0,
+  GRPC_HTTP2_PROTOCOL_ERROR = 0x1,
+  GRPC_HTTP2_INTERNAL_ERROR = 0x2,
+  GRPC_HTTP2_FLOW_CONTROL_ERROR = 0x3,
+  GRPC_HTTP2_SETTINGS_TIMEOUT = 0x4,
+  GRPC_HTTP2_STREAM_CLOSED = 0x5,
+  GRPC_HTTP2_FRAME_SIZE_ERROR = 0x6,
+  GRPC_HTTP2_REFUSED_STREAM = 0x7,
+  GRPC_HTTP2_CANCEL = 0x8,
+  GRPC_HTTP2_COMPRESSION_ERROR = 0x9,
+  GRPC_HTTP2_CONNECT_ERROR = 0xa,
+  GRPC_HTTP2_ENHANCE_YOUR_CALM = 0xb,
+  GRPC_HTTP2_INADEQUATE_SECURITY = 0xc,
   /* force use of a default clause */
-  GRPC_CHTTP2__ERROR_DO_NOT_USE = -1
-} grpc_chttp2_error_code;
+  GRPC_HTTP2__ERROR_DO_NOT_USE = -1
+} grpc_http2_error_code;
 
 #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_ERRORS_H */
diff --git a/src/core/lib/transport/status_conversion.c b/src/core/lib/transport/status_conversion.c
index eb1d53c..af0ac89 100644
--- a/src/core/lib/transport/status_conversion.c
+++ b/src/core/lib/transport/status_conversion.c
@@ -33,49 +33,49 @@
 
 #include "src/core/lib/transport/status_conversion.h"
 
-int grpc_chttp2_grpc_status_to_http2_error(grpc_status_code status) {
+int grpc_status_to_http2_error(grpc_status_code status) {
   switch (status) {
     case GRPC_STATUS_OK:
-      return GRPC_CHTTP2_NO_ERROR;
+      return GRPC_HTTP2_NO_ERROR;
     case GRPC_STATUS_CANCELLED:
-      return GRPC_CHTTP2_CANCEL;
+      return GRPC_HTTP2_CANCEL;
     case GRPC_STATUS_DEADLINE_EXCEEDED:
-      return GRPC_CHTTP2_CANCEL;
+      return GRPC_HTTP2_CANCEL;
     case GRPC_STATUS_RESOURCE_EXHAUSTED:
-      return GRPC_CHTTP2_ENHANCE_YOUR_CALM;
+      return GRPC_HTTP2_ENHANCE_YOUR_CALM;
     case GRPC_STATUS_PERMISSION_DENIED:
-      return GRPC_CHTTP2_INADEQUATE_SECURITY;
+      return GRPC_HTTP2_INADEQUATE_SECURITY;
     case GRPC_STATUS_UNAVAILABLE:
-      return GRPC_CHTTP2_REFUSED_STREAM;
+      return GRPC_HTTP2_REFUSED_STREAM;
     default:
-      return GRPC_CHTTP2_INTERNAL_ERROR;
+      return GRPC_HTTP2_INTERNAL_ERROR;
   }
 }
 
-grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
-    grpc_chttp2_error_code error, gpr_timespec deadline) {
+grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error,
+                                                 gpr_timespec deadline) {
   switch (error) {
-    case GRPC_CHTTP2_NO_ERROR:
+    case GRPC_HTTP2_NO_ERROR:
       /* should never be received */
       return GRPC_STATUS_INTERNAL;
-    case GRPC_CHTTP2_CANCEL:
+    case GRPC_HTTP2_CANCEL:
       /* http2 cancel translates to STATUS_CANCELLED iff deadline hasn't been
        * exceeded */
       return gpr_time_cmp(gpr_now(deadline.clock_type), deadline) >= 0
                  ? GRPC_STATUS_DEADLINE_EXCEEDED
                  : GRPC_STATUS_CANCELLED;
-    case GRPC_CHTTP2_ENHANCE_YOUR_CALM:
+    case GRPC_HTTP2_ENHANCE_YOUR_CALM:
       return GRPC_STATUS_RESOURCE_EXHAUSTED;
-    case GRPC_CHTTP2_INADEQUATE_SECURITY:
+    case GRPC_HTTP2_INADEQUATE_SECURITY:
       return GRPC_STATUS_PERMISSION_DENIED;
-    case GRPC_CHTTP2_REFUSED_STREAM:
+    case GRPC_HTTP2_REFUSED_STREAM:
       return GRPC_STATUS_UNAVAILABLE;
     default:
       return GRPC_STATUS_INTERNAL;
   }
 }
 
-grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status) {
+grpc_status_code grpc_http2_status_to_grpc_status(int status) {
   switch (status) {
     /* these HTTP2 status codes are called out explicitly in status.proto */
     case 200:
@@ -110,6 +110,4 @@
   }
 }
 
-int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status) {
-  return 200;
-}
+int grpc_status_to_http2_status(grpc_status_code status) { return 200; }
diff --git a/src/core/lib/transport/status_conversion.h b/src/core/lib/transport/status_conversion.h
index 5924115..3885ac9 100644
--- a/src/core/lib/transport/status_conversion.h
+++ b/src/core/lib/transport/status_conversion.h
@@ -38,13 +38,12 @@
 #include "src/core/lib/transport/http2_errors.h"
 
 /* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */
-grpc_chttp2_error_code grpc_chttp2_grpc_status_to_http2_error(
-    grpc_status_code status);
-grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
-    grpc_chttp2_error_code error, gpr_timespec deadline);
+grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status);
+grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error,
+                                                 gpr_timespec deadline);
 
 /* Conversion of HTTP status codes (:status) to grpc status codes */
-grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status);
-int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status);
+grpc_status_code grpc_http2_status_to_grpc_status(int status);
+int grpc_status_to_http2_status(grpc_status_code status);
 
 #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STATUS_CONVERSION_H */
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index 2dbb842..9a0abe1 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -181,13 +181,8 @@
   grpc_connectivity_state *connectivity_state;
   /** should the transport be disconnected */
   grpc_error *disconnect_with_error;
-  /** should we send a goaway?
-      after a goaway is sent, once there are no more active calls on
-      the transport, the transport should disconnect */
-  bool send_goaway;
   /** what should the goaway contain? */
-  grpc_status_code goaway_status;
-  grpc_slice *goaway_message;
+  grpc_error *goaway_error;
   /** set the callback for accepting new streams;
       this is a permanent callback, unlike the other one-shot closures.
       If true, the callback is set to set_accept_stream_fn, with its
diff --git a/src/core/lib/transport/transport_op_string.c b/src/core/lib/transport/transport_op_string.c
index 33bd35b..8810a6b 100644
--- a/src/core/lib/transport/transport_op_string.c
+++ b/src/core/lib/transport/transport_op_string.c
@@ -163,15 +163,12 @@
     grpc_error_free_string(err);
   }
 
-  if (op->send_goaway) {
+  if (op->goaway_error) {
     if (!first) gpr_strvec_add(&b, gpr_strdup(" "));
     first = false;
-    char *msg = op->goaway_message == NULL
-                    ? "null"
-                    : grpc_dump_slice(*op->goaway_message,
-                                      GPR_DUMP_ASCII | GPR_DUMP_HEX);
-    gpr_asprintf(&tmp, "SEND_GOAWAY:status=%d:msg=%s", op->goaway_status, msg);
-    if (op->goaway_message != NULL) gpr_free(msg);
+    const char *msg = grpc_error_string(op->goaway_error);
+    gpr_asprintf(&tmp, "SEND_GOAWAY:%s", msg);
+    grpc_error_free_string(msg);
     gpr_strvec_add(&b, tmp);
   }