Merge github.com:grpc/grpc into error
diff --git a/BUILD b/BUILD
index 793c1c7..9d2d90c 100644
--- a/BUILD
+++ b/BUILD
@@ -178,6 +178,7 @@
     "src/core/lib/iomgr/closure.h",
     "src/core/lib/iomgr/endpoint.h",
     "src/core/lib/iomgr/endpoint_pair.h",
+    "src/core/lib/iomgr/error.h",
     "src/core/lib/iomgr/ev_poll_and_epoll_posix.h",
     "src/core/lib/iomgr/ev_poll_posix.h",
     "src/core/lib/iomgr/ev_posix.h",
@@ -256,15 +257,25 @@
     "src/core/ext/transport/chttp2/transport/timeout_encoding.h",
     "src/core/ext/transport/chttp2/transport/varint.h",
     "src/core/ext/transport/chttp2/alpn/alpn.h",
-    "src/core/lib/security/auth_filters.h",
-    "src/core/lib/security/b64.h",
-    "src/core/lib/security/credentials.h",
-    "src/core/lib/security/handshake.h",
-    "src/core/lib/security/json_token.h",
-    "src/core/lib/security/jwt_verifier.h",
-    "src/core/lib/security/secure_endpoint.h",
-    "src/core/lib/security/security_connector.h",
-    "src/core/lib/security/security_context.h",
+    "src/core/lib/security/context/security_context.h",
+    "src/core/lib/security/credentials/composite/composite_credentials.h",
+    "src/core/lib/security/credentials/credentials.h",
+    "src/core/lib/security/credentials/fake/fake_credentials.h",
+    "src/core/lib/security/credentials/google_default/google_default_credentials.h",
+    "src/core/lib/security/credentials/iam/iam_credentials.h",
+    "src/core/lib/security/credentials/jwt/json_token.h",
+    "src/core/lib/security/credentials/jwt/jwt_credentials.h",
+    "src/core/lib/security/credentials/jwt/jwt_verifier.h",
+    "src/core/lib/security/credentials/oauth2/oauth2_credentials.h",
+    "src/core/lib/security/credentials/plugin/plugin_credentials.h",
+    "src/core/lib/security/credentials/ssl/ssl_credentials.h",
+    "src/core/lib/security/transport/auth_filters.h",
+    "src/core/lib/security/transport/handshake.h",
+    "src/core/lib/security/transport/secure_endpoint.h",
+    "src/core/lib/security/transport/security_connector.h",
+    "src/core/lib/security/transport/tsi_error.h",
+    "src/core/lib/security/util/b64.h",
+    "src/core/lib/security/util/json_util.h",
     "src/core/lib/tsi/fake_transport_security.h",
     "src/core/lib/tsi/ssl_transport_security.h",
     "src/core/lib/tsi/ssl_types.h",
@@ -313,6 +324,7 @@
     "src/core/lib/iomgr/endpoint.c",
     "src/core/lib/iomgr/endpoint_pair_posix.c",
     "src/core/lib/iomgr/endpoint_pair_windows.c",
+    "src/core/lib/iomgr/error.c",
     "src/core/lib/iomgr/ev_poll_and_epoll_posix.c",
     "src/core/lib/iomgr/ev_poll_posix.c",
     "src/core/lib/iomgr/ev_posix.c",
@@ -402,20 +414,29 @@
     "src/core/ext/transport/chttp2/transport/writing.c",
     "src/core/ext/transport/chttp2/alpn/alpn.c",
     "src/core/lib/http/httpcli_security_connector.c",
-    "src/core/lib/security/b64.c",
-    "src/core/lib/security/client_auth_filter.c",
-    "src/core/lib/security/credentials.c",
-    "src/core/lib/security/credentials_metadata.c",
-    "src/core/lib/security/credentials_posix.c",
-    "src/core/lib/security/credentials_win32.c",
-    "src/core/lib/security/google_default_credentials.c",
-    "src/core/lib/security/handshake.c",
-    "src/core/lib/security/json_token.c",
-    "src/core/lib/security/jwt_verifier.c",
-    "src/core/lib/security/secure_endpoint.c",
-    "src/core/lib/security/security_connector.c",
-    "src/core/lib/security/security_context.c",
-    "src/core/lib/security/server_auth_filter.c",
+    "src/core/lib/security/context/security_context.c",
+    "src/core/lib/security/credentials/composite/composite_credentials.c",
+    "src/core/lib/security/credentials/credentials.c",
+    "src/core/lib/security/credentials/credentials_metadata.c",
+    "src/core/lib/security/credentials/fake/fake_credentials.c",
+    "src/core/lib/security/credentials/google_default/credentials_posix.c",
+    "src/core/lib/security/credentials/google_default/credentials_win32.c",
+    "src/core/lib/security/credentials/google_default/google_default_credentials.c",
+    "src/core/lib/security/credentials/iam/iam_credentials.c",
+    "src/core/lib/security/credentials/jwt/json_token.c",
+    "src/core/lib/security/credentials/jwt/jwt_credentials.c",
+    "src/core/lib/security/credentials/jwt/jwt_verifier.c",
+    "src/core/lib/security/credentials/oauth2/oauth2_credentials.c",
+    "src/core/lib/security/credentials/plugin/plugin_credentials.c",
+    "src/core/lib/security/credentials/ssl/ssl_credentials.c",
+    "src/core/lib/security/transport/client_auth_filter.c",
+    "src/core/lib/security/transport/handshake.c",
+    "src/core/lib/security/transport/secure_endpoint.c",
+    "src/core/lib/security/transport/security_connector.c",
+    "src/core/lib/security/transport/server_auth_filter.c",
+    "src/core/lib/security/transport/tsi_error.c",
+    "src/core/lib/security/util/b64.c",
+    "src/core/lib/security/util/json_util.c",
     "src/core/lib/surface/init_secure.c",
     "src/core/lib/tsi/fake_transport_security.c",
     "src/core/lib/tsi/ssl_transport_security.c",
@@ -531,6 +552,7 @@
     "src/core/lib/iomgr/closure.h",
     "src/core/lib/iomgr/endpoint.h",
     "src/core/lib/iomgr/endpoint_pair.h",
+    "src/core/lib/iomgr/error.h",
     "src/core/lib/iomgr/ev_poll_and_epoll_posix.h",
     "src/core/lib/iomgr/ev_poll_posix.h",
     "src/core/lib/iomgr/ev_posix.h",
@@ -652,6 +674,7 @@
     "src/core/lib/iomgr/endpoint.c",
     "src/core/lib/iomgr/endpoint_pair_posix.c",
     "src/core/lib/iomgr/endpoint_pair_windows.c",
+    "src/core/lib/iomgr/error.c",
     "src/core/lib/iomgr/ev_poll_and_epoll_posix.c",
     "src/core/lib/iomgr/ev_poll_posix.c",
     "src/core/lib/iomgr/ev_posix.c",
@@ -822,26 +845,6 @@
 
 
 cc_library(
-  name = "grpc_zookeeper",
-  srcs = [
-    "src/core/ext/resolver/zookeeper/zookeeper_resolver.c",
-  ],
-  hdrs = [
-    "include/grpc/grpc_zookeeper.h",
-  ],
-  includes = [
-    "include",
-    ".",
-  ],
-  deps = [
-    ":gpr",
-    ":grpc",
-  ],
-)
-
-
-
-cc_library(
   name = "grpc++",
   srcs = [
     "src/cpp/client/secure_credentials.h",
@@ -1345,6 +1348,7 @@
     "src/core/lib/iomgr/endpoint.c",
     "src/core/lib/iomgr/endpoint_pair_posix.c",
     "src/core/lib/iomgr/endpoint_pair_windows.c",
+    "src/core/lib/iomgr/error.c",
     "src/core/lib/iomgr/ev_poll_and_epoll_posix.c",
     "src/core/lib/iomgr/ev_poll_posix.c",
     "src/core/lib/iomgr/ev_posix.c",
@@ -1434,20 +1438,29 @@
     "src/core/ext/transport/chttp2/transport/writing.c",
     "src/core/ext/transport/chttp2/alpn/alpn.c",
     "src/core/lib/http/httpcli_security_connector.c",
-    "src/core/lib/security/b64.c",
-    "src/core/lib/security/client_auth_filter.c",
-    "src/core/lib/security/credentials.c",
-    "src/core/lib/security/credentials_metadata.c",
-    "src/core/lib/security/credentials_posix.c",
-    "src/core/lib/security/credentials_win32.c",
-    "src/core/lib/security/google_default_credentials.c",
-    "src/core/lib/security/handshake.c",
-    "src/core/lib/security/json_token.c",
-    "src/core/lib/security/jwt_verifier.c",
-    "src/core/lib/security/secure_endpoint.c",
-    "src/core/lib/security/security_connector.c",
-    "src/core/lib/security/security_context.c",
-    "src/core/lib/security/server_auth_filter.c",
+    "src/core/lib/security/context/security_context.c",
+    "src/core/lib/security/credentials/composite/composite_credentials.c",
+    "src/core/lib/security/credentials/credentials.c",
+    "src/core/lib/security/credentials/credentials_metadata.c",
+    "src/core/lib/security/credentials/fake/fake_credentials.c",
+    "src/core/lib/security/credentials/google_default/credentials_posix.c",
+    "src/core/lib/security/credentials/google_default/credentials_win32.c",
+    "src/core/lib/security/credentials/google_default/google_default_credentials.c",
+    "src/core/lib/security/credentials/iam/iam_credentials.c",
+    "src/core/lib/security/credentials/jwt/json_token.c",
+    "src/core/lib/security/credentials/jwt/jwt_credentials.c",
+    "src/core/lib/security/credentials/jwt/jwt_verifier.c",
+    "src/core/lib/security/credentials/oauth2/oauth2_credentials.c",
+    "src/core/lib/security/credentials/plugin/plugin_credentials.c",
+    "src/core/lib/security/credentials/ssl/ssl_credentials.c",
+    "src/core/lib/security/transport/client_auth_filter.c",
+    "src/core/lib/security/transport/handshake.c",
+    "src/core/lib/security/transport/secure_endpoint.c",
+    "src/core/lib/security/transport/security_connector.c",
+    "src/core/lib/security/transport/server_auth_filter.c",
+    "src/core/lib/security/transport/tsi_error.c",
+    "src/core/lib/security/util/b64.c",
+    "src/core/lib/security/util/json_util.c",
     "src/core/lib/surface/init_secure.c",
     "src/core/lib/tsi/fake_transport_security.c",
     "src/core/lib/tsi/ssl_transport_security.c",
@@ -1542,6 +1555,7 @@
     "src/core/lib/iomgr/closure.h",
     "src/core/lib/iomgr/endpoint.h",
     "src/core/lib/iomgr/endpoint_pair.h",
+    "src/core/lib/iomgr/error.h",
     "src/core/lib/iomgr/ev_poll_and_epoll_posix.h",
     "src/core/lib/iomgr/ev_poll_posix.h",
     "src/core/lib/iomgr/ev_posix.h",
@@ -1620,15 +1634,25 @@
     "src/core/ext/transport/chttp2/transport/timeout_encoding.h",
     "src/core/ext/transport/chttp2/transport/varint.h",
     "src/core/ext/transport/chttp2/alpn/alpn.h",
-    "src/core/lib/security/auth_filters.h",
-    "src/core/lib/security/b64.h",
-    "src/core/lib/security/credentials.h",
-    "src/core/lib/security/handshake.h",
-    "src/core/lib/security/json_token.h",
-    "src/core/lib/security/jwt_verifier.h",
-    "src/core/lib/security/secure_endpoint.h",
-    "src/core/lib/security/security_connector.h",
-    "src/core/lib/security/security_context.h",
+    "src/core/lib/security/context/security_context.h",
+    "src/core/lib/security/credentials/composite/composite_credentials.h",
+    "src/core/lib/security/credentials/credentials.h",
+    "src/core/lib/security/credentials/fake/fake_credentials.h",
+    "src/core/lib/security/credentials/google_default/google_default_credentials.h",
+    "src/core/lib/security/credentials/iam/iam_credentials.h",
+    "src/core/lib/security/credentials/jwt/json_token.h",
+    "src/core/lib/security/credentials/jwt/jwt_credentials.h",
+    "src/core/lib/security/credentials/jwt/jwt_verifier.h",
+    "src/core/lib/security/credentials/oauth2/oauth2_credentials.h",
+    "src/core/lib/security/credentials/plugin/plugin_credentials.h",
+    "src/core/lib/security/credentials/ssl/ssl_credentials.h",
+    "src/core/lib/security/transport/auth_filters.h",
+    "src/core/lib/security/transport/handshake.h",
+    "src/core/lib/security/transport/secure_endpoint.h",
+    "src/core/lib/security/transport/security_connector.h",
+    "src/core/lib/security/transport/tsi_error.h",
+    "src/core/lib/security/util/b64.h",
+    "src/core/lib/security/util/json_util.h",
     "src/core/lib/tsi/fake_transport_security.h",
     "src/core/lib/tsi/ssl_transport_security.h",
     "src/core/lib/tsi/ssl_types.h",
diff --git a/Makefile b/Makefile
index 42cedf5..c0908c0 100644
--- a/Makefile
+++ b/Makefile
@@ -948,8 +948,9 @@
 hpack_parser_fuzzer_test: $(BINDIR)/$(CONFIG)/hpack_parser_fuzzer_test
 hpack_parser_test: $(BINDIR)/$(CONFIG)/hpack_parser_test
 hpack_table_test: $(BINDIR)/$(CONFIG)/hpack_table_test
-http_fuzzer_test: $(BINDIR)/$(CONFIG)/http_fuzzer_test
 http_parser_test: $(BINDIR)/$(CONFIG)/http_parser_test
+http_request_fuzzer_test: $(BINDIR)/$(CONFIG)/http_request_fuzzer_test
+http_response_fuzzer_test: $(BINDIR)/$(CONFIG)/http_response_fuzzer_test
 httpcli_format_request_test: $(BINDIR)/$(CONFIG)/httpcli_format_request_test
 httpcli_test: $(BINDIR)/$(CONFIG)/httpcli_test
 httpscli_test: $(BINDIR)/$(CONFIG)/httpscli_test
@@ -1052,7 +1053,6 @@
 sync_streaming_ping_pong_test: $(BINDIR)/$(CONFIG)/sync_streaming_ping_pong_test
 sync_unary_ping_pong_test: $(BINDIR)/$(CONFIG)/sync_unary_ping_pong_test
 thread_stress_test: $(BINDIR)/$(CONFIG)/thread_stress_test
-zookeeper_test: $(BINDIR)/$(CONFIG)/zookeeper_test
 public_headers_must_be_c89: $(BINDIR)/$(CONFIG)/public_headers_must_be_c89
 boringssl_aes_test: $(BINDIR)/$(CONFIG)/boringssl_aes_test
 boringssl_asn1_test: $(BINDIR)/$(CONFIG)/boringssl_asn1_test
@@ -1134,7 +1134,8 @@
 api_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/api_fuzzer_one_entry
 client_fuzzer_one_entry: $(BINDIR)/$(CONFIG)/client_fuzzer_one_entry
 hpack_parser_fuzzer_test_one_entry: $(BINDIR)/$(CONFIG)/hpack_parser_fuzzer_test_one_entry
-http_fuzzer_test_one_entry: $(BINDIR)/$(CONFIG)/http_fuzzer_test_one_entry
+http_request_fuzzer_test_one_entry: $(BINDIR)/$(CONFIG)/http_request_fuzzer_test_one_entry
+http_response_fuzzer_test_one_entry: $(BINDIR)/$(CONFIG)/http_response_fuzzer_test_one_entry
 json_fuzzer_test_one_entry: $(BINDIR)/$(CONFIG)/json_fuzzer_test_one_entry
 nanopb_fuzzer_response_test_one_entry: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_response_test_one_entry
 nanopb_fuzzer_serverlist_test_one_entry: $(BINDIR)/$(CONFIG)/nanopb_fuzzer_serverlist_test_one_entry
@@ -1180,8 +1181,8 @@
 
 shared_csharp: shared_c  $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc_csharp_ext$(SHARED_VERSION).$(SHARED_EXT)
 ifeq ($(HAS_ZOOKEEPER),true)
-static_zookeeper_libs: $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a
-shared_zookeeper_libs: $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT)
+static_zookeeper_libs:
+shared_zookeeper_libs:
 else
 
 static_zookeeper_libs:
@@ -1364,7 +1365,8 @@
   $(BINDIR)/$(CONFIG)/api_fuzzer_one_entry \
   $(BINDIR)/$(CONFIG)/client_fuzzer_one_entry \
   $(BINDIR)/$(CONFIG)/hpack_parser_fuzzer_test_one_entry \
-  $(BINDIR)/$(CONFIG)/http_fuzzer_test_one_entry \
+  $(BINDIR)/$(CONFIG)/http_request_fuzzer_test_one_entry \
+  $(BINDIR)/$(CONFIG)/http_response_fuzzer_test_one_entry \
   $(BINDIR)/$(CONFIG)/json_fuzzer_test_one_entry \
   $(BINDIR)/$(CONFIG)/nanopb_fuzzer_response_test_one_entry \
   $(BINDIR)/$(CONFIG)/nanopb_fuzzer_serverlist_test_one_entry \
@@ -1463,7 +1465,6 @@
 
 ifeq ($(HAS_ZOOKEEPER),true)
 buildtests_zookeeper: privatelibs_zookeeper \
- $(BINDIR)/$(CONFIG)/zookeeper_test \
 
 else
 buildtests_zookeeper:
@@ -1814,8 +1815,6 @@
 	$(E) "[STRIP]   Stripping libgrpc_unsecure.a"
 	$(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a
 ifeq ($(HAS_ZOOKEEPER),true)
-	$(E) "[STRIP]   Stripping libgrpc_zookeeper.a"
-	$(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a
 endif
 endif
 
@@ -1836,8 +1835,6 @@
 	$(E) "[STRIP]   Stripping $(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION).$(SHARED_EXT)"
 	$(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION).$(SHARED_EXT)
 ifeq ($(HAS_ZOOKEEPER),true)
-	$(E) "[STRIP]   Stripping $(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT)"
-	$(Q) $(STRIP) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT)
 endif
 endif
 
@@ -2162,9 +2159,6 @@
 	$(Q) $(INSTALL) -d $(prefix)/lib
 	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(prefix)/lib/libgrpc_unsecure.a
 ifeq ($(HAS_ZOOKEEPER),true)
-	$(E) "[INSTALL] Installing libgrpc_zookeeper.a"
-	$(Q) $(INSTALL) -d $(prefix)/lib
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a $(prefix)/lib/libgrpc_zookeeper.a
 endif
 
 install-static_cxx: static_cxx strip-static_cxx install-pkg-config_cxx
@@ -2206,15 +2200,6 @@
 	$(Q) ln -sf $(SHARED_PREFIX)grpc_unsecure$(SHARED_VERSION).$(SHARED_EXT) $(prefix)/lib/libgrpc_unsecure.so
 endif
 ifeq ($(HAS_ZOOKEEPER),true)
-	$(E) "[INSTALL] Installing $(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT)"
-	$(Q) $(INSTALL) -d $(prefix)/lib
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/$(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT) $(prefix)/lib/$(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT)
-ifeq ($(SYSTEM),MINGW32)
-	$(Q) $(INSTALL) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper-imp.a $(prefix)/lib/libgrpc_zookeeper-imp.a
-else ifneq ($(SYSTEM),Darwin)
-	$(Q) ln -sf $(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT) $(prefix)/lib/libgrpc_zookeeper.so.0
-	$(Q) ln -sf $(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT) $(prefix)/lib/libgrpc_zookeeper.so
-endif
 endif
 ifneq ($(SYSTEM),MINGW32)
 ifneq ($(SYSTEM),Darwin)
@@ -2510,6 +2495,7 @@
     src/core/lib/iomgr/endpoint.c \
     src/core/lib/iomgr/endpoint_pair_posix.c \
     src/core/lib/iomgr/endpoint_pair_windows.c \
+    src/core/lib/iomgr/error.c \
     src/core/lib/iomgr/ev_poll_and_epoll_posix.c \
     src/core/lib/iomgr/ev_poll_posix.c \
     src/core/lib/iomgr/ev_posix.c \
@@ -2599,20 +2585,29 @@
     src/core/ext/transport/chttp2/transport/writing.c \
     src/core/ext/transport/chttp2/alpn/alpn.c \
     src/core/lib/http/httpcli_security_connector.c \
-    src/core/lib/security/b64.c \
-    src/core/lib/security/client_auth_filter.c \
-    src/core/lib/security/credentials.c \
-    src/core/lib/security/credentials_metadata.c \
-    src/core/lib/security/credentials_posix.c \
-    src/core/lib/security/credentials_win32.c \
-    src/core/lib/security/google_default_credentials.c \
-    src/core/lib/security/handshake.c \
-    src/core/lib/security/json_token.c \
-    src/core/lib/security/jwt_verifier.c \
-    src/core/lib/security/secure_endpoint.c \
-    src/core/lib/security/security_connector.c \
-    src/core/lib/security/security_context.c \
-    src/core/lib/security/server_auth_filter.c \
+    src/core/lib/security/context/security_context.c \
+    src/core/lib/security/credentials/composite/composite_credentials.c \
+    src/core/lib/security/credentials/credentials.c \
+    src/core/lib/security/credentials/credentials_metadata.c \
+    src/core/lib/security/credentials/fake/fake_credentials.c \
+    src/core/lib/security/credentials/google_default/credentials_posix.c \
+    src/core/lib/security/credentials/google_default/credentials_win32.c \
+    src/core/lib/security/credentials/google_default/google_default_credentials.c \
+    src/core/lib/security/credentials/iam/iam_credentials.c \
+    src/core/lib/security/credentials/jwt/json_token.c \
+    src/core/lib/security/credentials/jwt/jwt_credentials.c \
+    src/core/lib/security/credentials/jwt/jwt_verifier.c \
+    src/core/lib/security/credentials/oauth2/oauth2_credentials.c \
+    src/core/lib/security/credentials/plugin/plugin_credentials.c \
+    src/core/lib/security/credentials/ssl/ssl_credentials.c \
+    src/core/lib/security/transport/client_auth_filter.c \
+    src/core/lib/security/transport/handshake.c \
+    src/core/lib/security/transport/secure_endpoint.c \
+    src/core/lib/security/transport/security_connector.c \
+    src/core/lib/security/transport/server_auth_filter.c \
+    src/core/lib/security/transport/tsi_error.c \
+    src/core/lib/security/util/b64.c \
+    src/core/lib/security/util/json_util.c \
     src/core/lib/surface/init_secure.c \
     src/core/lib/tsi/fake_transport_security.c \
     src/core/lib/tsi/ssl_transport_security.c \
@@ -2857,6 +2852,7 @@
     src/core/lib/iomgr/endpoint.c \
     src/core/lib/iomgr/endpoint_pair_posix.c \
     src/core/lib/iomgr/endpoint_pair_windows.c \
+    src/core/lib/iomgr/error.c \
     src/core/lib/iomgr/ev_poll_and_epoll_posix.c \
     src/core/lib/iomgr/ev_poll_posix.c \
     src/core/lib/iomgr/ev_posix.c \
@@ -3051,49 +3047,6 @@
 endif
 
 
-LIBGRPC_ZOOKEEPER_SRC = \
-    src/core/ext/resolver/zookeeper/zookeeper_resolver.c \
-
-PUBLIC_HEADERS_C += \
-    include/grpc/grpc_zookeeper.h \
-
-LIBGRPC_ZOOKEEPER_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGRPC_ZOOKEEPER_SRC))))
-
-
-$(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a: $(ZLIB_DEP)  $(LIBGRPC_ZOOKEEPER_OBJS) 
-	$(E) "[AR]      Creating $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a
-	$(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a $(LIBGRPC_ZOOKEEPER_OBJS) 
-ifeq ($(SYSTEM),Darwin)
-	$(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a
-endif
-
-
-
-ifeq ($(SYSTEM),MINGW32)
-$(LIBDIR)/$(CONFIG)/grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT): $(LIBGRPC_ZOOKEEPER_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/gpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/grpc.$(SHARED_EXT)
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared grpc_zookeeper.def -Wl,--output-def=$(LIBDIR)/$(CONFIG)/grpc_zookeeper$(SHARED_VERSION).def -Wl,--out-implib=$(LIBDIR)/$(CONFIG)/libgrpc_zookeeper$(SHARED_VERSION)-dll.a -o $(LIBDIR)/$(CONFIG)/grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT) $(LIBGRPC_ZOOKEEPER_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) -lgpr-imp -lgrpc-imp
-else
-$(LIBDIR)/$(CONFIG)/libgrpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT): $(LIBGRPC_ZOOKEEPER_OBJS)  $(ZLIB_DEP) $(LIBDIR)/$(CONFIG)/libgpr.$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc.$(SHARED_EXT)
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-ifeq ($(SYSTEM),Darwin)
-	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -install_name $(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT) -dynamiclib -o $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT) $(LIBGRPC_ZOOKEEPER_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) -lgpr -lgrpc -lzookeeper_mt
-else
-	$(Q) $(LD) $(LDFLAGS) -L$(LIBDIR)/$(CONFIG) -shared -Wl,-soname,libgrpc_zookeeper.so.0 -o $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT) $(LIBGRPC_ZOOKEEPER_OBJS) $(LDLIBS) $(ZLIB_MERGE_LIBS) -lgpr -lgrpc -lzookeeper_mt
-	$(Q) ln -sf $(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper$(SHARED_VERSION).so.0
-	$(Q) ln -sf $(SHARED_PREFIX)grpc_zookeeper$(SHARED_VERSION).$(SHARED_EXT) $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper$(SHARED_VERSION).so
-endif
-endif
-
-ifneq ($(NO_DEPS),true)
--include $(LIBGRPC_ZOOKEEPER_OBJS:.o=.dep)
-endif
-
-
 LIBRECONNECT_SERVER_SRC = \
     test/core/util/reconnect_server.c \
 
@@ -7117,14 +7070,14 @@
 
 
 
-$(BINDIR)/$(CONFIG)/gpr_load_file_test: $(GPR_LOAD_FILE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(BINDIR)/$(CONFIG)/gpr_load_file_test: $(GPR_LOAD_FILE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(GPR_LOAD_FILE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_load_file_test
+	$(Q) $(LD) $(LDFLAGS) $(GPR_LOAD_FILE_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/gpr_load_file_test
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/core/support/load_file_test.o:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/core/support/load_file_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
 deps_gpr_load_file_test: $(GPR_LOAD_FILE_TEST_OBJS:.o=.dep)
 
@@ -8031,38 +7984,6 @@
 endif
 
 
-HTTP_FUZZER_TEST_SRC = \
-    test/core/http/fuzzer.c \
-
-HTTP_FUZZER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HTTP_FUZZER_TEST_SRC))))
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL.
-
-$(BINDIR)/$(CONFIG)/http_fuzzer_test: openssl_dep_error
-
-else
-
-
-
-$(BINDIR)/$(CONFIG)/http_fuzzer_test: $(HTTP_FUZZER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) $(HTTP_FUZZER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -lFuzzer -o $(BINDIR)/$(CONFIG)/http_fuzzer_test
-
-endif
-
-$(OBJDIR)/$(CONFIG)/test/core/http/fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-deps_http_fuzzer_test: $(HTTP_FUZZER_TEST_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(HTTP_FUZZER_TEST_OBJS:.o=.dep)
-endif
-endif
-
-
 HTTP_PARSER_TEST_SRC = \
     test/core/http/parser_test.c \
 
@@ -8095,6 +8016,70 @@
 endif
 
 
+HTTP_REQUEST_FUZZER_TEST_SRC = \
+    test/core/http/request_fuzzer.c \
+
+HTTP_REQUEST_FUZZER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HTTP_REQUEST_FUZZER_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/http_request_fuzzer_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/http_request_fuzzer_test: $(HTTP_REQUEST_FUZZER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(HTTP_REQUEST_FUZZER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -lFuzzer -o $(BINDIR)/$(CONFIG)/http_request_fuzzer_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/http/request_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_http_request_fuzzer_test: $(HTTP_REQUEST_FUZZER_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(HTTP_REQUEST_FUZZER_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
+HTTP_RESPONSE_FUZZER_TEST_SRC = \
+    test/core/http/response_fuzzer.c \
+
+HTTP_RESPONSE_FUZZER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HTTP_RESPONSE_FUZZER_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/http_response_fuzzer_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/http_response_fuzzer_test: $(HTTP_RESPONSE_FUZZER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LDXX) $(LDFLAGS) $(HTTP_RESPONSE_FUZZER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -lFuzzer -o $(BINDIR)/$(CONFIG)/http_response_fuzzer_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/http/response_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_http_response_fuzzer_test: $(HTTP_RESPONSE_FUZZER_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(HTTP_RESPONSE_FUZZER_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 HTTPCLI_FORMAT_REQUEST_TEST_SRC = \
     test/core/http/format_request_test.c \
 
@@ -11963,53 +11948,6 @@
 endif
 
 
-ZOOKEEPER_TEST_SRC = \
-    $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc \
-    test/cpp/end2end/zookeeper_test.cc \
-
-ZOOKEEPER_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(ZOOKEEPER_TEST_SRC))))
-ifeq ($(NO_SECURE),true)
-
-# You can't build secure targets if you don't have OpenSSL.
-
-$(BINDIR)/$(CONFIG)/zookeeper_test: openssl_dep_error
-
-else
-
-
-
-
-ifeq ($(NO_PROTOBUF),true)
-
-# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.
-
-$(BINDIR)/$(CONFIG)/zookeeper_test: protobuf_dep_error
-
-else
-
-$(BINDIR)/$(CONFIG)/zookeeper_test: $(PROTOBUF_DEP) $(ZOOKEEPER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-	$(E) "[LD]      Linking $@"
-	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LDXX) $(LDFLAGS) $(ZOOKEEPER_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a -lzookeeper_mt $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/zookeeper_test
-
-endif
-
-endif
-
-$(OBJDIR)/$(CONFIG)/src/proto/grpc/testing/echo.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-$(OBJDIR)/$(CONFIG)/test/cpp/end2end/zookeeper_test.o:  $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBDIR)/$(CONFIG)/libgrpc_zookeeper.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
-
-deps_zookeeper_test: $(ZOOKEEPER_TEST_OBJS:.o=.dep)
-
-ifneq ($(NO_SECURE),true)
-ifneq ($(NO_DEPS),true)
--include $(ZOOKEEPER_TEST_OBJS:.o=.dep)
-endif
-endif
-$(OBJDIR)/$(CONFIG)/test/cpp/end2end/zookeeper_test.o: $(GENDIR)/src/proto/grpc/testing/echo.pb.cc $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc
-
-
 PUBLIC_HEADERS_MUST_BE_C89_SRC = \
     test/core/surface/public_headers_must_be_c89.c \
 
@@ -14185,37 +14123,72 @@
 endif
 
 
-HTTP_FUZZER_TEST_ONE_ENTRY_SRC = \
-    test/core/http/fuzzer.c \
+HTTP_REQUEST_FUZZER_TEST_ONE_ENTRY_SRC = \
+    test/core/http/request_fuzzer.c \
     test/core/util/one_corpus_entry_fuzzer.c \
 
-HTTP_FUZZER_TEST_ONE_ENTRY_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HTTP_FUZZER_TEST_ONE_ENTRY_SRC))))
+HTTP_REQUEST_FUZZER_TEST_ONE_ENTRY_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HTTP_REQUEST_FUZZER_TEST_ONE_ENTRY_SRC))))
 ifeq ($(NO_SECURE),true)
 
 # You can't build secure targets if you don't have OpenSSL.
 
-$(BINDIR)/$(CONFIG)/http_fuzzer_test_one_entry: openssl_dep_error
+$(BINDIR)/$(CONFIG)/http_request_fuzzer_test_one_entry: openssl_dep_error
 
 else
 
 
 
-$(BINDIR)/$(CONFIG)/http_fuzzer_test_one_entry: $(HTTP_FUZZER_TEST_ONE_ENTRY_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(BINDIR)/$(CONFIG)/http_request_fuzzer_test_one_entry: $(HTTP_REQUEST_FUZZER_TEST_ONE_ENTRY_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 	$(E) "[LD]      Linking $@"
 	$(Q) mkdir -p `dirname $@`
-	$(Q) $(LD) $(LDFLAGS) $(HTTP_FUZZER_TEST_ONE_ENTRY_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/http_fuzzer_test_one_entry
+	$(Q) $(LD) $(LDFLAGS) $(HTTP_REQUEST_FUZZER_TEST_ONE_ENTRY_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/http_request_fuzzer_test_one_entry
 
 endif
 
-$(OBJDIR)/$(CONFIG)/test/core/http/fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+$(OBJDIR)/$(CONFIG)/test/core/http/request_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
 $(OBJDIR)/$(CONFIG)/test/core/util/one_corpus_entry_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
 
-deps_http_fuzzer_test_one_entry: $(HTTP_FUZZER_TEST_ONE_ENTRY_OBJS:.o=.dep)
+deps_http_request_fuzzer_test_one_entry: $(HTTP_REQUEST_FUZZER_TEST_ONE_ENTRY_OBJS:.o=.dep)
 
 ifneq ($(NO_SECURE),true)
 ifneq ($(NO_DEPS),true)
--include $(HTTP_FUZZER_TEST_ONE_ENTRY_OBJS:.o=.dep)
+-include $(HTTP_REQUEST_FUZZER_TEST_ONE_ENTRY_OBJS:.o=.dep)
+endif
+endif
+
+
+HTTP_RESPONSE_FUZZER_TEST_ONE_ENTRY_SRC = \
+    test/core/http/response_fuzzer.c \
+    test/core/util/one_corpus_entry_fuzzer.c \
+
+HTTP_RESPONSE_FUZZER_TEST_ONE_ENTRY_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(HTTP_RESPONSE_FUZZER_TEST_ONE_ENTRY_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/http_response_fuzzer_test_one_entry: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/http_response_fuzzer_test_one_entry: $(HTTP_RESPONSE_FUZZER_TEST_ONE_ENTRY_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(HTTP_RESPONSE_FUZZER_TEST_ONE_ENTRY_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/http_response_fuzzer_test_one_entry
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/http/response_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+$(OBJDIR)/$(CONFIG)/test/core/util/one_corpus_entry_fuzzer.o:  $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
+
+deps_http_response_fuzzer_test_one_entry: $(HTTP_RESPONSE_FUZZER_TEST_ONE_ENTRY_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(HTTP_RESPONSE_FUZZER_TEST_ONE_ENTRY_OBJS:.o=.dep)
 endif
 endif
 
@@ -14409,20 +14382,29 @@
 src/core/ext/transport/cronet/transport/cronet_api_dummy.c: $(OPENSSL_DEP)
 src/core/ext/transport/cronet/transport/cronet_transport.c: $(OPENSSL_DEP)
 src/core/lib/http/httpcli_security_connector.c: $(OPENSSL_DEP)
-src/core/lib/security/b64.c: $(OPENSSL_DEP)
-src/core/lib/security/client_auth_filter.c: $(OPENSSL_DEP)
-src/core/lib/security/credentials.c: $(OPENSSL_DEP)
-src/core/lib/security/credentials_metadata.c: $(OPENSSL_DEP)
-src/core/lib/security/credentials_posix.c: $(OPENSSL_DEP)
-src/core/lib/security/credentials_win32.c: $(OPENSSL_DEP)
-src/core/lib/security/google_default_credentials.c: $(OPENSSL_DEP)
-src/core/lib/security/handshake.c: $(OPENSSL_DEP)
-src/core/lib/security/json_token.c: $(OPENSSL_DEP)
-src/core/lib/security/jwt_verifier.c: $(OPENSSL_DEP)
-src/core/lib/security/secure_endpoint.c: $(OPENSSL_DEP)
-src/core/lib/security/security_connector.c: $(OPENSSL_DEP)
-src/core/lib/security/security_context.c: $(OPENSSL_DEP)
-src/core/lib/security/server_auth_filter.c: $(OPENSSL_DEP)
+src/core/lib/security/context/security_context.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/composite/composite_credentials.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/credentials.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/credentials_metadata.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/fake/fake_credentials.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/google_default/credentials_posix.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/google_default/credentials_win32.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/google_default/google_default_credentials.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/iam/iam_credentials.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/jwt/json_token.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/jwt/jwt_credentials.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/jwt/jwt_verifier.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/oauth2/oauth2_credentials.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/plugin/plugin_credentials.c: $(OPENSSL_DEP)
+src/core/lib/security/credentials/ssl/ssl_credentials.c: $(OPENSSL_DEP)
+src/core/lib/security/transport/client_auth_filter.c: $(OPENSSL_DEP)
+src/core/lib/security/transport/handshake.c: $(OPENSSL_DEP)
+src/core/lib/security/transport/secure_endpoint.c: $(OPENSSL_DEP)
+src/core/lib/security/transport/security_connector.c: $(OPENSSL_DEP)
+src/core/lib/security/transport/server_auth_filter.c: $(OPENSSL_DEP)
+src/core/lib/security/transport/tsi_error.c: $(OPENSSL_DEP)
+src/core/lib/security/util/b64.c: $(OPENSSL_DEP)
+src/core/lib/security/util/json_util.c: $(OPENSSL_DEP)
 src/core/lib/surface/init_secure.c: $(OPENSSL_DEP)
 src/core/lib/tsi/fake_transport_security.c: $(OPENSSL_DEP)
 src/core/lib/tsi/ssl_transport_security.c: $(OPENSSL_DEP)
diff --git a/binding.gyp b/binding.gyp
index 760bb24..cfc509f 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -581,6 +581,7 @@
         'src/core/lib/iomgr/endpoint.c',
         'src/core/lib/iomgr/endpoint_pair_posix.c',
         'src/core/lib/iomgr/endpoint_pair_windows.c',
+        'src/core/lib/iomgr/error.c',
         'src/core/lib/iomgr/ev_poll_and_epoll_posix.c',
         'src/core/lib/iomgr/ev_poll_posix.c',
         'src/core/lib/iomgr/ev_posix.c',
@@ -670,20 +671,29 @@
         'src/core/ext/transport/chttp2/transport/writing.c',
         'src/core/ext/transport/chttp2/alpn/alpn.c',
         'src/core/lib/http/httpcli_security_connector.c',
-        'src/core/lib/security/b64.c',
-        'src/core/lib/security/client_auth_filter.c',
-        'src/core/lib/security/credentials.c',
-        'src/core/lib/security/credentials_metadata.c',
-        'src/core/lib/security/credentials_posix.c',
-        'src/core/lib/security/credentials_win32.c',
-        'src/core/lib/security/google_default_credentials.c',
-        'src/core/lib/security/handshake.c',
-        'src/core/lib/security/json_token.c',
-        'src/core/lib/security/jwt_verifier.c',
-        'src/core/lib/security/secure_endpoint.c',
-        'src/core/lib/security/security_connector.c',
-        'src/core/lib/security/security_context.c',
-        'src/core/lib/security/server_auth_filter.c',
+        'src/core/lib/security/context/security_context.c',
+        'src/core/lib/security/credentials/composite/composite_credentials.c',
+        'src/core/lib/security/credentials/credentials.c',
+        'src/core/lib/security/credentials/credentials_metadata.c',
+        'src/core/lib/security/credentials/fake/fake_credentials.c',
+        'src/core/lib/security/credentials/google_default/credentials_posix.c',
+        'src/core/lib/security/credentials/google_default/credentials_win32.c',
+        'src/core/lib/security/credentials/google_default/google_default_credentials.c',
+        'src/core/lib/security/credentials/iam/iam_credentials.c',
+        'src/core/lib/security/credentials/jwt/json_token.c',
+        'src/core/lib/security/credentials/jwt/jwt_credentials.c',
+        'src/core/lib/security/credentials/jwt/jwt_verifier.c',
+        'src/core/lib/security/credentials/oauth2/oauth2_credentials.c',
+        'src/core/lib/security/credentials/plugin/plugin_credentials.c',
+        'src/core/lib/security/credentials/ssl/ssl_credentials.c',
+        'src/core/lib/security/transport/client_auth_filter.c',
+        'src/core/lib/security/transport/handshake.c',
+        'src/core/lib/security/transport/secure_endpoint.c',
+        'src/core/lib/security/transport/security_connector.c',
+        'src/core/lib/security/transport/server_auth_filter.c',
+        'src/core/lib/security/transport/tsi_error.c',
+        'src/core/lib/security/util/b64.c',
+        'src/core/lib/security/util/json_util.c',
         'src/core/lib/surface/init_secure.c',
         'src/core/lib/tsi/fake_transport_security.c',
         'src/core/lib/tsi/ssl_transport_security.c',
diff --git a/build.yaml b/build.yaml
index ac61612..c876416 100644
--- a/build.yaml
+++ b/build.yaml
@@ -165,6 +165,7 @@
   - src/core/lib/iomgr/closure.h
   - src/core/lib/iomgr/endpoint.h
   - src/core/lib/iomgr/endpoint_pair.h
+  - src/core/lib/iomgr/error.h
   - src/core/lib/iomgr/ev_poll_and_epoll_posix.h
   - src/core/lib/iomgr/ev_poll_posix.h
   - src/core/lib/iomgr/ev_posix.h
@@ -240,6 +241,7 @@
   - src/core/lib/iomgr/endpoint.c
   - src/core/lib/iomgr/endpoint_pair_posix.c
   - src/core/lib/iomgr/endpoint_pair_windows.c
+  - src/core/lib/iomgr/error.c
   - src/core/lib/iomgr/ev_poll_and_epoll_posix.c
   - src/core/lib/iomgr/ev_poll_posix.c
   - src/core/lib/iomgr/ev_posix.c
@@ -406,31 +408,50 @@
   - include/grpc/grpc_security.h
   - include/grpc/grpc_security_constants.h
   headers:
-  - src/core/lib/security/auth_filters.h
-  - src/core/lib/security/b64.h
-  - src/core/lib/security/credentials.h
-  - src/core/lib/security/handshake.h
-  - src/core/lib/security/json_token.h
-  - src/core/lib/security/jwt_verifier.h
-  - src/core/lib/security/secure_endpoint.h
-  - src/core/lib/security/security_connector.h
-  - src/core/lib/security/security_context.h
+  - src/core/lib/security/context/security_context.h
+  - src/core/lib/security/credentials/composite/composite_credentials.h
+  - src/core/lib/security/credentials/credentials.h
+  - src/core/lib/security/credentials/fake/fake_credentials.h
+  - src/core/lib/security/credentials/google_default/google_default_credentials.h
+  - src/core/lib/security/credentials/iam/iam_credentials.h
+  - src/core/lib/security/credentials/jwt/json_token.h
+  - src/core/lib/security/credentials/jwt/jwt_credentials.h
+  - src/core/lib/security/credentials/jwt/jwt_verifier.h
+  - src/core/lib/security/credentials/oauth2/oauth2_credentials.h
+  - src/core/lib/security/credentials/plugin/plugin_credentials.h
+  - src/core/lib/security/credentials/ssl/ssl_credentials.h
+  - src/core/lib/security/transport/auth_filters.h
+  - src/core/lib/security/transport/handshake.h
+  - src/core/lib/security/transport/secure_endpoint.h
+  - src/core/lib/security/transport/security_connector.h
+  - src/core/lib/security/transport/tsi_error.h
+  - src/core/lib/security/util/b64.h
+  - src/core/lib/security/util/json_util.h
   src:
   - src/core/lib/http/httpcli_security_connector.c
-  - src/core/lib/security/b64.c
-  - src/core/lib/security/client_auth_filter.c
-  - src/core/lib/security/credentials.c
-  - src/core/lib/security/credentials_metadata.c
-  - src/core/lib/security/credentials_posix.c
-  - src/core/lib/security/credentials_win32.c
-  - src/core/lib/security/google_default_credentials.c
-  - src/core/lib/security/handshake.c
-  - src/core/lib/security/json_token.c
-  - src/core/lib/security/jwt_verifier.c
-  - src/core/lib/security/secure_endpoint.c
-  - src/core/lib/security/security_connector.c
-  - src/core/lib/security/security_context.c
-  - src/core/lib/security/server_auth_filter.c
+  - src/core/lib/security/context/security_context.c
+  - src/core/lib/security/credentials/composite/composite_credentials.c
+  - src/core/lib/security/credentials/credentials.c
+  - src/core/lib/security/credentials/credentials_metadata.c
+  - src/core/lib/security/credentials/fake/fake_credentials.c
+  - src/core/lib/security/credentials/google_default/credentials_posix.c
+  - src/core/lib/security/credentials/google_default/credentials_win32.c
+  - src/core/lib/security/credentials/google_default/google_default_credentials.c
+  - src/core/lib/security/credentials/iam/iam_credentials.c
+  - src/core/lib/security/credentials/jwt/json_token.c
+  - src/core/lib/security/credentials/jwt/jwt_credentials.c
+  - src/core/lib/security/credentials/jwt/jwt_verifier.c
+  - src/core/lib/security/credentials/oauth2/oauth2_credentials.c
+  - src/core/lib/security/credentials/plugin/plugin_credentials.c
+  - src/core/lib/security/credentials/ssl/ssl_credentials.c
+  - src/core/lib/security/transport/client_auth_filter.c
+  - src/core/lib/security/transport/handshake.c
+  - src/core/lib/security/transport/secure_endpoint.c
+  - src/core/lib/security/transport/security_connector.c
+  - src/core/lib/security/transport/server_auth_filter.c
+  - src/core/lib/security/transport/tsi_error.c
+  - src/core/lib/security/util/b64.c
+  - src/core/lib/security/util/json_util.c
   - src/core/lib/surface/init_secure.c
   secure: true
   uses:
@@ -836,21 +857,6 @@
   generate_plugin_registry: true
   secure: false
   vs_project_guid: '{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}'
-- name: grpc_zookeeper
-  build: all
-  language: c
-  public_headers:
-  - include/grpc/grpc_zookeeper.h
-  src:
-  - src/core/ext/resolver/zookeeper/zookeeper_resolver.c
-  deps:
-  - gpr
-  - grpc
-  external_deps:
-  - zookeeper
-  platforms:
-  - linux
-  secure: false
 - name: reconnect_server
   build: private
   language: c
@@ -1473,6 +1479,7 @@
   src:
   - test/core/support/load_file_test.c
   deps:
+  - grpc
   - gpr_test_util
   - gpr
 - name: gpr_log_test
@@ -1746,19 +1753,6 @@
   - grpc
   - gpr_test_util
   - gpr
-- name: http_fuzzer_test
-  build: fuzzer
-  language: c
-  src:
-  - test/core/http/fuzzer.c
-  deps:
-  - grpc_test_util
-  - grpc
-  - gpr_test_util
-  - gpr
-  corpus_dirs:
-  - test/core/http/corpus
-  maxlen: 2048
 - name: http_parser_test
   build: test
   language: c
@@ -1769,6 +1763,32 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: http_request_fuzzer_test
+  build: fuzzer
+  language: c
+  src:
+  - test/core/http/request_fuzzer.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+  corpus_dirs:
+  - test/core/http/corpus
+  maxlen: 2048
+- name: http_response_fuzzer_test
+  build: fuzzer
+  language: c
+  src:
+  - test/core/http/response_fuzzer.c
+  deps:
+  - grpc_test_util
+  - grpc
+  - gpr_test_util
+  - gpr
+  corpus_dirs:
+  - test/core/http/corpus
+  maxlen: 2048
 - name: httpcli_format_request_test
   build: test
   language: c
@@ -3090,26 +3110,6 @@
   - grpc
   - gpr_test_util
   - gpr
-- name: zookeeper_test
-  gtest: true
-  build: test
-  run: false
-  language: c++
-  src:
-  - src/proto/grpc/testing/echo.proto
-  - test/cpp/end2end/zookeeper_test.cc
-  deps:
-  - grpc++_test_util
-  - grpc_test_util
-  - grpc++
-  - grpc_zookeeper
-  - grpc
-  - gpr_test_util
-  - gpr
-  external_deps:
-  - zookeeper
-  platforms:
-  - linux
 - name: public_headers_must_be_c89
   build: test
   language: c89
diff --git a/config.m4 b/config.m4
index 6ed1887..0230353 100644
--- a/config.m4
+++ b/config.m4
@@ -100,6 +100,7 @@
     src/core/lib/iomgr/endpoint.c \
     src/core/lib/iomgr/endpoint_pair_posix.c \
     src/core/lib/iomgr/endpoint_pair_windows.c \
+    src/core/lib/iomgr/error.c \
     src/core/lib/iomgr/ev_poll_and_epoll_posix.c \
     src/core/lib/iomgr/ev_poll_posix.c \
     src/core/lib/iomgr/ev_posix.c \
@@ -189,20 +190,29 @@
     src/core/ext/transport/chttp2/transport/writing.c \
     src/core/ext/transport/chttp2/alpn/alpn.c \
     src/core/lib/http/httpcli_security_connector.c \
-    src/core/lib/security/b64.c \
-    src/core/lib/security/client_auth_filter.c \
-    src/core/lib/security/credentials.c \
-    src/core/lib/security/credentials_metadata.c \
-    src/core/lib/security/credentials_posix.c \
-    src/core/lib/security/credentials_win32.c \
-    src/core/lib/security/google_default_credentials.c \
-    src/core/lib/security/handshake.c \
-    src/core/lib/security/json_token.c \
-    src/core/lib/security/jwt_verifier.c \
-    src/core/lib/security/secure_endpoint.c \
-    src/core/lib/security/security_connector.c \
-    src/core/lib/security/security_context.c \
-    src/core/lib/security/server_auth_filter.c \
+    src/core/lib/security/context/security_context.c \
+    src/core/lib/security/credentials/composite/composite_credentials.c \
+    src/core/lib/security/credentials/credentials.c \
+    src/core/lib/security/credentials/credentials_metadata.c \
+    src/core/lib/security/credentials/fake/fake_credentials.c \
+    src/core/lib/security/credentials/google_default/credentials_posix.c \
+    src/core/lib/security/credentials/google_default/credentials_win32.c \
+    src/core/lib/security/credentials/google_default/google_default_credentials.c \
+    src/core/lib/security/credentials/iam/iam_credentials.c \
+    src/core/lib/security/credentials/jwt/json_token.c \
+    src/core/lib/security/credentials/jwt/jwt_credentials.c \
+    src/core/lib/security/credentials/jwt/jwt_verifier.c \
+    src/core/lib/security/credentials/oauth2/oauth2_credentials.c \
+    src/core/lib/security/credentials/plugin/plugin_credentials.c \
+    src/core/lib/security/credentials/ssl/ssl_credentials.c \
+    src/core/lib/security/transport/client_auth_filter.c \
+    src/core/lib/security/transport/handshake.c \
+    src/core/lib/security/transport/secure_endpoint.c \
+    src/core/lib/security/transport/security_connector.c \
+    src/core/lib/security/transport/server_auth_filter.c \
+    src/core/lib/security/transport/tsi_error.c \
+    src/core/lib/security/util/b64.c \
+    src/core/lib/security/util/json_util.c \
     src/core/lib/surface/init_secure.c \
     src/core/lib/tsi/fake_transport_security.c \
     src/core/lib/tsi/ssl_transport_security.c \
@@ -579,7 +589,18 @@
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/iomgr)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/json)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/profiling)
-  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/context)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/composite)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/fake)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/google_default)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/iam)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/jwt)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/oauth2)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/plugin)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/credentials/ssl)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/transport)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/security/util)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/support)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/surface)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/transport)
diff --git a/gRPC.podspec b/gRPC.podspec
index 67e7a81..d8c6abc 100644
--- a/gRPC.podspec
+++ b/gRPC.podspec
@@ -181,6 +181,7 @@
                       'src/core/lib/iomgr/closure.h',
                       'src/core/lib/iomgr/endpoint.h',
                       'src/core/lib/iomgr/endpoint_pair.h',
+                      'src/core/lib/iomgr/error.h',
                       'src/core/lib/iomgr/ev_poll_and_epoll_posix.h',
                       'src/core/lib/iomgr/ev_poll_posix.h',
                       'src/core/lib/iomgr/ev_posix.h',
@@ -259,15 +260,25 @@
                       'src/core/ext/transport/chttp2/transport/timeout_encoding.h',
                       'src/core/ext/transport/chttp2/transport/varint.h',
                       'src/core/ext/transport/chttp2/alpn/alpn.h',
-                      'src/core/lib/security/auth_filters.h',
-                      'src/core/lib/security/b64.h',
-                      'src/core/lib/security/credentials.h',
-                      'src/core/lib/security/handshake.h',
-                      'src/core/lib/security/json_token.h',
-                      'src/core/lib/security/jwt_verifier.h',
-                      'src/core/lib/security/secure_endpoint.h',
-                      'src/core/lib/security/security_connector.h',
-                      'src/core/lib/security/security_context.h',
+                      'src/core/lib/security/context/security_context.h',
+                      'src/core/lib/security/credentials/composite/composite_credentials.h',
+                      'src/core/lib/security/credentials/credentials.h',
+                      'src/core/lib/security/credentials/fake/fake_credentials.h',
+                      'src/core/lib/security/credentials/google_default/google_default_credentials.h',
+                      'src/core/lib/security/credentials/iam/iam_credentials.h',
+                      'src/core/lib/security/credentials/jwt/json_token.h',
+                      'src/core/lib/security/credentials/jwt/jwt_credentials.h',
+                      'src/core/lib/security/credentials/jwt/jwt_verifier.h',
+                      'src/core/lib/security/credentials/oauth2/oauth2_credentials.h',
+                      'src/core/lib/security/credentials/plugin/plugin_credentials.h',
+                      'src/core/lib/security/credentials/ssl/ssl_credentials.h',
+                      'src/core/lib/security/transport/auth_filters.h',
+                      'src/core/lib/security/transport/handshake.h',
+                      'src/core/lib/security/transport/secure_endpoint.h',
+                      'src/core/lib/security/transport/security_connector.h',
+                      'src/core/lib/security/transport/tsi_error.h',
+                      'src/core/lib/security/util/b64.h',
+                      'src/core/lib/security/util/json_util.h',
                       'src/core/lib/tsi/fake_transport_security.h',
                       'src/core/lib/tsi/ssl_transport_security.h',
                       'src/core/lib/tsi/ssl_types.h',
@@ -350,6 +361,7 @@
                       'src/core/lib/iomgr/endpoint.c',
                       'src/core/lib/iomgr/endpoint_pair_posix.c',
                       'src/core/lib/iomgr/endpoint_pair_windows.c',
+                      'src/core/lib/iomgr/error.c',
                       'src/core/lib/iomgr/ev_poll_and_epoll_posix.c',
                       'src/core/lib/iomgr/ev_poll_posix.c',
                       'src/core/lib/iomgr/ev_posix.c',
@@ -439,20 +451,29 @@
                       'src/core/ext/transport/chttp2/transport/writing.c',
                       'src/core/ext/transport/chttp2/alpn/alpn.c',
                       'src/core/lib/http/httpcli_security_connector.c',
-                      'src/core/lib/security/b64.c',
-                      'src/core/lib/security/client_auth_filter.c',
-                      'src/core/lib/security/credentials.c',
-                      'src/core/lib/security/credentials_metadata.c',
-                      'src/core/lib/security/credentials_posix.c',
-                      'src/core/lib/security/credentials_win32.c',
-                      'src/core/lib/security/google_default_credentials.c',
-                      'src/core/lib/security/handshake.c',
-                      'src/core/lib/security/json_token.c',
-                      'src/core/lib/security/jwt_verifier.c',
-                      'src/core/lib/security/secure_endpoint.c',
-                      'src/core/lib/security/security_connector.c',
-                      'src/core/lib/security/security_context.c',
-                      'src/core/lib/security/server_auth_filter.c',
+                      'src/core/lib/security/context/security_context.c',
+                      'src/core/lib/security/credentials/composite/composite_credentials.c',
+                      'src/core/lib/security/credentials/credentials.c',
+                      'src/core/lib/security/credentials/credentials_metadata.c',
+                      'src/core/lib/security/credentials/fake/fake_credentials.c',
+                      'src/core/lib/security/credentials/google_default/credentials_posix.c',
+                      'src/core/lib/security/credentials/google_default/credentials_win32.c',
+                      'src/core/lib/security/credentials/google_default/google_default_credentials.c',
+                      'src/core/lib/security/credentials/iam/iam_credentials.c',
+                      'src/core/lib/security/credentials/jwt/json_token.c',
+                      'src/core/lib/security/credentials/jwt/jwt_credentials.c',
+                      'src/core/lib/security/credentials/jwt/jwt_verifier.c',
+                      'src/core/lib/security/credentials/oauth2/oauth2_credentials.c',
+                      'src/core/lib/security/credentials/plugin/plugin_credentials.c',
+                      'src/core/lib/security/credentials/ssl/ssl_credentials.c',
+                      'src/core/lib/security/transport/client_auth_filter.c',
+                      'src/core/lib/security/transport/handshake.c',
+                      'src/core/lib/security/transport/secure_endpoint.c',
+                      'src/core/lib/security/transport/security_connector.c',
+                      'src/core/lib/security/transport/server_auth_filter.c',
+                      'src/core/lib/security/transport/tsi_error.c',
+                      'src/core/lib/security/util/b64.c',
+                      'src/core/lib/security/util/json_util.c',
                       'src/core/lib/surface/init_secure.c',
                       'src/core/lib/tsi/fake_transport_security.c',
                       'src/core/lib/tsi/ssl_transport_security.c',
@@ -531,6 +552,7 @@
                               'src/core/lib/iomgr/closure.h',
                               'src/core/lib/iomgr/endpoint.h',
                               'src/core/lib/iomgr/endpoint_pair.h',
+                              'src/core/lib/iomgr/error.h',
                               'src/core/lib/iomgr/ev_poll_and_epoll_posix.h',
                               'src/core/lib/iomgr/ev_poll_posix.h',
                               'src/core/lib/iomgr/ev_posix.h',
@@ -609,15 +631,25 @@
                               'src/core/ext/transport/chttp2/transport/timeout_encoding.h',
                               'src/core/ext/transport/chttp2/transport/varint.h',
                               'src/core/ext/transport/chttp2/alpn/alpn.h',
-                              'src/core/lib/security/auth_filters.h',
-                              'src/core/lib/security/b64.h',
-                              'src/core/lib/security/credentials.h',
-                              'src/core/lib/security/handshake.h',
-                              'src/core/lib/security/json_token.h',
-                              'src/core/lib/security/jwt_verifier.h',
-                              'src/core/lib/security/secure_endpoint.h',
-                              'src/core/lib/security/security_connector.h',
-                              'src/core/lib/security/security_context.h',
+                              'src/core/lib/security/context/security_context.h',
+                              'src/core/lib/security/credentials/composite/composite_credentials.h',
+                              'src/core/lib/security/credentials/credentials.h',
+                              'src/core/lib/security/credentials/fake/fake_credentials.h',
+                              'src/core/lib/security/credentials/google_default/google_default_credentials.h',
+                              'src/core/lib/security/credentials/iam/iam_credentials.h',
+                              'src/core/lib/security/credentials/jwt/json_token.h',
+                              'src/core/lib/security/credentials/jwt/jwt_credentials.h',
+                              'src/core/lib/security/credentials/jwt/jwt_verifier.h',
+                              'src/core/lib/security/credentials/oauth2/oauth2_credentials.h',
+                              'src/core/lib/security/credentials/plugin/plugin_credentials.h',
+                              'src/core/lib/security/credentials/ssl/ssl_credentials.h',
+                              'src/core/lib/security/transport/auth_filters.h',
+                              'src/core/lib/security/transport/handshake.h',
+                              'src/core/lib/security/transport/secure_endpoint.h',
+                              'src/core/lib/security/transport/security_connector.h',
+                              'src/core/lib/security/transport/tsi_error.h',
+                              'src/core/lib/security/util/b64.h',
+                              'src/core/lib/security/util/json_util.h',
                               'src/core/lib/tsi/fake_transport_security.h',
                               'src/core/lib/tsi/ssl_transport_security.h',
                               'src/core/lib/tsi/ssl_types.h',
diff --git a/grpc.def b/grpc.def
index 09a94a6..552bf75 100644
--- a/grpc.def
+++ b/grpc.def
@@ -220,6 +220,8 @@
     gpr_avl_add
     gpr_avl_remove
     gpr_avl_get
+    gpr_avl_maybe_get
+    gpr_avl_is_empty
     gpr_cmdline_create
     gpr_cmdline_add_int
     gpr_cmdline_add_flag
diff --git a/grpc.gemspec b/grpc.gemspec
index 13aed6b..2ab0514 100755
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -190,6 +190,7 @@
   s.files += %w( src/core/lib/iomgr/closure.h )
   s.files += %w( src/core/lib/iomgr/endpoint.h )
   s.files += %w( src/core/lib/iomgr/endpoint_pair.h )
+  s.files += %w( src/core/lib/iomgr/error.h )
   s.files += %w( src/core/lib/iomgr/ev_poll_and_epoll_posix.h )
   s.files += %w( src/core/lib/iomgr/ev_poll_posix.h )
   s.files += %w( src/core/lib/iomgr/ev_posix.h )
@@ -268,15 +269,25 @@
   s.files += %w( src/core/ext/transport/chttp2/transport/timeout_encoding.h )
   s.files += %w( src/core/ext/transport/chttp2/transport/varint.h )
   s.files += %w( src/core/ext/transport/chttp2/alpn/alpn.h )
-  s.files += %w( src/core/lib/security/auth_filters.h )
-  s.files += %w( src/core/lib/security/b64.h )
-  s.files += %w( src/core/lib/security/credentials.h )
-  s.files += %w( src/core/lib/security/handshake.h )
-  s.files += %w( src/core/lib/security/json_token.h )
-  s.files += %w( src/core/lib/security/jwt_verifier.h )
-  s.files += %w( src/core/lib/security/secure_endpoint.h )
-  s.files += %w( src/core/lib/security/security_connector.h )
-  s.files += %w( src/core/lib/security/security_context.h )
+  s.files += %w( src/core/lib/security/context/security_context.h )
+  s.files += %w( src/core/lib/security/credentials/composite/composite_credentials.h )
+  s.files += %w( src/core/lib/security/credentials/credentials.h )
+  s.files += %w( src/core/lib/security/credentials/fake/fake_credentials.h )
+  s.files += %w( src/core/lib/security/credentials/google_default/google_default_credentials.h )
+  s.files += %w( src/core/lib/security/credentials/iam/iam_credentials.h )
+  s.files += %w( src/core/lib/security/credentials/jwt/json_token.h )
+  s.files += %w( src/core/lib/security/credentials/jwt/jwt_credentials.h )
+  s.files += %w( src/core/lib/security/credentials/jwt/jwt_verifier.h )
+  s.files += %w( src/core/lib/security/credentials/oauth2/oauth2_credentials.h )
+  s.files += %w( src/core/lib/security/credentials/plugin/plugin_credentials.h )
+  s.files += %w( src/core/lib/security/credentials/ssl/ssl_credentials.h )
+  s.files += %w( src/core/lib/security/transport/auth_filters.h )
+  s.files += %w( src/core/lib/security/transport/handshake.h )
+  s.files += %w( src/core/lib/security/transport/secure_endpoint.h )
+  s.files += %w( src/core/lib/security/transport/security_connector.h )
+  s.files += %w( src/core/lib/security/transport/tsi_error.h )
+  s.files += %w( src/core/lib/security/util/b64.h )
+  s.files += %w( src/core/lib/security/util/json_util.h )
   s.files += %w( src/core/lib/tsi/fake_transport_security.h )
   s.files += %w( src/core/lib/tsi/ssl_transport_security.h )
   s.files += %w( src/core/lib/tsi/ssl_types.h )
@@ -329,6 +340,7 @@
   s.files += %w( src/core/lib/iomgr/endpoint.c )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_posix.c )
   s.files += %w( src/core/lib/iomgr/endpoint_pair_windows.c )
+  s.files += %w( src/core/lib/iomgr/error.c )
   s.files += %w( src/core/lib/iomgr/ev_poll_and_epoll_posix.c )
   s.files += %w( src/core/lib/iomgr/ev_poll_posix.c )
   s.files += %w( src/core/lib/iomgr/ev_posix.c )
@@ -418,20 +430,29 @@
   s.files += %w( src/core/ext/transport/chttp2/transport/writing.c )
   s.files += %w( src/core/ext/transport/chttp2/alpn/alpn.c )
   s.files += %w( src/core/lib/http/httpcli_security_connector.c )
-  s.files += %w( src/core/lib/security/b64.c )
-  s.files += %w( src/core/lib/security/client_auth_filter.c )
-  s.files += %w( src/core/lib/security/credentials.c )
-  s.files += %w( src/core/lib/security/credentials_metadata.c )
-  s.files += %w( src/core/lib/security/credentials_posix.c )
-  s.files += %w( src/core/lib/security/credentials_win32.c )
-  s.files += %w( src/core/lib/security/google_default_credentials.c )
-  s.files += %w( src/core/lib/security/handshake.c )
-  s.files += %w( src/core/lib/security/json_token.c )
-  s.files += %w( src/core/lib/security/jwt_verifier.c )
-  s.files += %w( src/core/lib/security/secure_endpoint.c )
-  s.files += %w( src/core/lib/security/security_connector.c )
-  s.files += %w( src/core/lib/security/security_context.c )
-  s.files += %w( src/core/lib/security/server_auth_filter.c )
+  s.files += %w( src/core/lib/security/context/security_context.c )
+  s.files += %w( src/core/lib/security/credentials/composite/composite_credentials.c )
+  s.files += %w( src/core/lib/security/credentials/credentials.c )
+  s.files += %w( src/core/lib/security/credentials/credentials_metadata.c )
+  s.files += %w( src/core/lib/security/credentials/fake/fake_credentials.c )
+  s.files += %w( src/core/lib/security/credentials/google_default/credentials_posix.c )
+  s.files += %w( src/core/lib/security/credentials/google_default/credentials_win32.c )
+  s.files += %w( src/core/lib/security/credentials/google_default/google_default_credentials.c )
+  s.files += %w( src/core/lib/security/credentials/iam/iam_credentials.c )
+  s.files += %w( src/core/lib/security/credentials/jwt/json_token.c )
+  s.files += %w( src/core/lib/security/credentials/jwt/jwt_credentials.c )
+  s.files += %w( src/core/lib/security/credentials/jwt/jwt_verifier.c )
+  s.files += %w( src/core/lib/security/credentials/oauth2/oauth2_credentials.c )
+  s.files += %w( src/core/lib/security/credentials/plugin/plugin_credentials.c )
+  s.files += %w( src/core/lib/security/credentials/ssl/ssl_credentials.c )
+  s.files += %w( src/core/lib/security/transport/client_auth_filter.c )
+  s.files += %w( src/core/lib/security/transport/handshake.c )
+  s.files += %w( src/core/lib/security/transport/secure_endpoint.c )
+  s.files += %w( src/core/lib/security/transport/security_connector.c )
+  s.files += %w( src/core/lib/security/transport/server_auth_filter.c )
+  s.files += %w( src/core/lib/security/transport/tsi_error.c )
+  s.files += %w( src/core/lib/security/util/b64.c )
+  s.files += %w( src/core/lib/security/util/json_util.c )
   s.files += %w( src/core/lib/surface/init_secure.c )
   s.files += %w( src/core/lib/tsi/fake_transport_security.c )
   s.files += %w( src/core/lib/tsi/ssl_transport_security.c )
diff --git a/include/grpc/impl/codegen/log.h b/include/grpc/impl/codegen/log.h
index aa86fc4..c3f30a9 100644
--- a/include/grpc/impl/codegen/log.h
+++ b/include/grpc/impl/codegen/log.h
@@ -43,6 +43,10 @@
 extern "C" {
 #endif
 
+#ifdef GPR_WIN32
+#include <grpc/support/log_win32.h>
+#endif
+
 /* GPR log API.
 
    Usage (within grpc):
diff --git a/include/grpc/support/avl.h b/include/grpc/support/avl.h
index d71592d..f5bf32c 100644
--- a/include/grpc/support/avl.h
+++ b/include/grpc/support/avl.h
@@ -88,5 +88,10 @@
     does not mutate avl.
     returns NULL if key is not found. */
 GPRAPI void *gpr_avl_get(gpr_avl avl, void *key);
+/** Return 1 if avl contains key, 0 otherwise; if it has the key, sets *value to
+    its value*/
+GPRAPI int gpr_avl_maybe_get(gpr_avl avl, void *key, void **value);
+/** Return 1 if avl is empty, 0 otherwise */
+GPRAPI int gpr_avl_is_empty(gpr_avl avl);
 
 #endif /* GRPC_SUPPORT_AVL_H */
diff --git a/package.xml b/package.xml
index 33a769a..ee7cc1f 100644
--- a/package.xml
+++ b/package.xml
@@ -197,6 +197,7 @@
     <file baseinstalldir="/" name="src/core/lib/iomgr/closure.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/error.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_poll_and_epoll_posix.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_poll_posix.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_posix.h" role="src" />
@@ -275,15 +276,25 @@
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/timeout_encoding.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/varint.h" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/alpn/alpn.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/auth_filters.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/b64.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/credentials.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/handshake.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/json_token.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/jwt_verifier.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/secure_endpoint.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/security_connector.h" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/security_context.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/context/security_context.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/composite/composite_credentials.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/credentials.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/fake/fake_credentials.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/google_default/google_default_credentials.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/iam/iam_credentials.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/jwt/json_token.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/jwt/jwt_credentials.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/jwt/jwt_verifier.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/oauth2/oauth2_credentials.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/plugin/plugin_credentials.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/ssl/ssl_credentials.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/auth_filters.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/handshake.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/secure_endpoint.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/security_connector.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/tsi_error.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/util/b64.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/util/json_util.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/tsi/fake_transport_security.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/tsi/ssl_transport_security.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/tsi/ssl_types.h" role="src" />
@@ -336,6 +347,7 @@
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_posix.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/endpoint_pair_windows.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/error.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_poll_and_epoll_posix.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_poll_posix.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/ev_posix.c" role="src" />
@@ -425,20 +437,29 @@
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/transport/writing.c" role="src" />
     <file baseinstalldir="/" name="src/core/ext/transport/chttp2/alpn/alpn.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/http/httpcli_security_connector.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/b64.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/client_auth_filter.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/credentials.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/credentials_metadata.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/credentials_posix.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/credentials_win32.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/google_default_credentials.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/handshake.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/json_token.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/jwt_verifier.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/secure_endpoint.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/security_connector.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/security_context.c" role="src" />
-    <file baseinstalldir="/" name="src/core/lib/security/server_auth_filter.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/context/security_context.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/composite/composite_credentials.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/credentials.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/credentials_metadata.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/fake/fake_credentials.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/google_default/credentials_posix.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/google_default/credentials_win32.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/google_default/google_default_credentials.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/iam/iam_credentials.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/jwt/json_token.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/jwt/jwt_credentials.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/jwt/jwt_verifier.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/oauth2/oauth2_credentials.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/plugin/plugin_credentials.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/credentials/ssl/ssl_credentials.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/client_auth_filter.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/handshake.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/secure_endpoint.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/security_connector.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/server_auth_filter.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/transport/tsi_error.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/util/b64.c" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/security/util/json_util.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/surface/init_secure.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/tsi/fake_transport_security.c" role="src" />
     <file baseinstalldir="/" name="src/core/lib/tsi/ssl_transport_security.c" role="src" />
diff --git a/src/core/ext/census/grpc_filter.c b/src/core/ext/census/grpc_filter.c
index 5e278ef..df7016e 100644
--- a/src/core/ext/census/grpc_filter.c
+++ b/src/core/ext/census/grpc_filter.c
@@ -91,14 +91,14 @@
 }
 
 static void server_on_done_recv(grpc_exec_ctx *exec_ctx, void *ptr,
-                                bool success) {
+                                grpc_error *error) {
   grpc_call_element *elem = ptr;
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     extract_and_annotate_method_tag(calld->recv_initial_metadata, calld, chand);
   }
-  calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
+  calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, error);
 }
 
 static void server_mutate_op(grpc_call_element *elem,
diff --git a/src/core/ext/client_config/channel_connectivity.c b/src/core/ext/client_config/channel_connectivity.c
index 3ebc333..b452cc3 100644
--- a/src/core/ext/client_config/channel_connectivity.c
+++ b/src/core/ext/client_config/channel_connectivity.c
@@ -75,7 +75,7 @@
 typedef struct {
   gpr_mu mu;
   callback_phase phase;
-  int success;
+  grpc_error *error;
   grpc_closure on_complete;
   grpc_timer alarm;
   grpc_connectivity_state state;
@@ -95,6 +95,7 @@
     abort();
   }
   gpr_mu_destroy(&w->mu);
+  GRPC_ERROR_UNREF(w->error);
   gpr_free(w);
 }
 
@@ -122,7 +123,7 @@
 }
 
 static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w,
-                        int due_to_completion) {
+                        bool due_to_completion, grpc_error *error) {
   int delete = 0;
 
   if (due_to_completion) {
@@ -130,14 +131,18 @@
   }
 
   gpr_mu_lock(&w->mu);
+  const char *msg = grpc_error_string(error);
+  grpc_error_free_string(msg);
+
   if (due_to_completion) {
-    w->success = 1;
+    GRPC_ERROR_UNREF(w->error);
+    w->error = GRPC_ERROR_NONE;
   }
   switch (w->phase) {
     case WAITING:
       w->phase = CALLING_BACK;
-      grpc_cq_end_op(exec_ctx, w->cq, w->tag, w->success, finished_completion,
-                     w, &w->completion_storage);
+      grpc_cq_end_op(exec_ctx, w->cq, w->tag, GRPC_ERROR_REF(w->error),
+                     finished_completion, w, &w->completion_storage);
       break;
     case CALLING_BACK:
       w->phase = CALLING_BACK_AND_FINISHED;
@@ -153,14 +158,18 @@
   if (delete) {
     delete_state_watcher(exec_ctx, w);
   }
+
+  GRPC_ERROR_UNREF(error);
 }
 
-static void watch_complete(grpc_exec_ctx *exec_ctx, void *pw, bool success) {
-  partly_done(exec_ctx, pw, 1);
+static void watch_complete(grpc_exec_ctx *exec_ctx, void *pw,
+                           grpc_error *error) {
+  partly_done(exec_ctx, pw, true, GRPC_ERROR_REF(error));
 }
 
-static void timeout_complete(grpc_exec_ctx *exec_ctx, void *pw, bool success) {
-  partly_done(exec_ctx, pw, 0);
+static void timeout_complete(grpc_exec_ctx *exec_ctx, void *pw,
+                             grpc_error *error) {
+  partly_done(exec_ctx, pw, false, GRPC_ERROR_REF(error));
 }
 
 void grpc_channel_watch_connectivity_state(
@@ -185,7 +194,7 @@
   grpc_closure_init(&w->on_complete, watch_complete, w);
   w->phase = WAITING;
   w->state = last_observed_state;
-  w->success = 0;
+  w->error = GRPC_ERROR_CREATE("Timeout waiting for channel state");
   w->cq = cq;
   w->tag = tag;
   w->channel = channel;
diff --git a/src/core/ext/client_config/client_channel.c b/src/core/ext/client_config/client_channel.c
index 9b5a078..d00b966 100644
--- a/src/core/ext/client_config/client_channel.c
+++ b/src/core/ext/client_config/client_channel.c
@@ -117,6 +117,7 @@
 static void set_channel_connectivity_state_locked(grpc_exec_ctx *exec_ctx,
                                                   channel_data *chand,
                                                   grpc_connectivity_state state,
+                                                  grpc_error *error,
                                                   const char *reason) {
   if ((state == GRPC_CHANNEL_TRANSIENT_FAILURE ||
        state == GRPC_CHANNEL_FATAL_FAILURE) &&
@@ -127,11 +128,13 @@
         /* mask= */ GRPC_INITIAL_METADATA_IGNORE_CONNECTIVITY,
         /* check= */ 0);
   }
-  grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, state, reason);
+  grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, state, error,
+                              reason);
 }
 
-static void on_lb_policy_state_changed_locked(
-    grpc_exec_ctx *exec_ctx, lb_policy_connectivity_watcher *w) {
+static void on_lb_policy_state_changed_locked(grpc_exec_ctx *exec_ctx,
+                                              lb_policy_connectivity_watcher *w,
+                                              grpc_error *error) {
   grpc_connectivity_state publish_state = w->state;
   /* check if the notification is for a stale policy */
   if (w->lb_policy != w->chand->lb_policy) return;
@@ -144,18 +147,18 @@
     w->chand->lb_policy = NULL;
   }
   set_channel_connectivity_state_locked(exec_ctx, w->chand, publish_state,
-                                        "lb_changed");
+                                        GRPC_ERROR_REF(error), "lb_changed");
   if (w->state != GRPC_CHANNEL_FATAL_FAILURE) {
     watch_lb_policy(exec_ctx, w->chand, w->lb_policy, w->state);
   }
 }
 
 static void on_lb_policy_state_changed(grpc_exec_ctx *exec_ctx, void *arg,
-                                       bool iomgr_success) {
+                                       grpc_error *error) {
   lb_policy_connectivity_watcher *w = arg;
 
   gpr_mu_lock(&w->chand->mu_config);
-  on_lb_policy_state_changed_locked(exec_ctx, w);
+  on_lb_policy_state_changed_locked(exec_ctx, w, error);
   gpr_mu_unlock(&w->chand->mu_config);
 
   GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, "watch_lb_policy");
@@ -177,19 +180,22 @@
 }
 
 static void cc_on_config_changed(grpc_exec_ctx *exec_ctx, void *arg,
-                                 bool iomgr_success) {
+                                 grpc_error *error) {
   channel_data *chand = arg;
   grpc_lb_policy *lb_policy = NULL;
   grpc_lb_policy *old_lb_policy;
   grpc_connectivity_state state = GRPC_CHANNEL_TRANSIENT_FAILURE;
   int exit_idle = 0;
+  grpc_error *state_error = GRPC_ERROR_CREATE("No load balancing policy");
 
   if (chand->incoming_configuration != NULL) {
     lb_policy = grpc_client_config_get_lb_policy(chand->incoming_configuration);
     if (lb_policy != NULL) {
       GRPC_LB_POLICY_REF(lb_policy, "channel");
       GRPC_LB_POLICY_REF(lb_policy, "config_change");
-      state = grpc_lb_policy_check_connectivity(exec_ctx, lb_policy);
+      GRPC_ERROR_UNREF(state_error);
+      state =
+          grpc_lb_policy_check_connectivity(exec_ctx, lb_policy, &state_error);
     }
 
     grpc_client_config_unref(exec_ctx, chand->incoming_configuration);
@@ -209,7 +215,9 @@
     grpc_exec_ctx_enqueue_list(exec_ctx, &chand->waiting_for_config_closures,
                                NULL);
   } else if (chand->resolver == NULL /* disconnected */) {
-    grpc_closure_list_fail_all(&chand->waiting_for_config_closures);
+    grpc_closure_list_fail_all(
+        &chand->waiting_for_config_closures,
+        GRPC_ERROR_CREATE_REFERENCING("Channel disconnected", &error, 1));
     grpc_exec_ctx_enqueue_list(exec_ctx, &chand->waiting_for_config_closures,
                                NULL);
   }
@@ -219,9 +227,9 @@
     chand->exit_idle_when_lb_policy_arrives = 0;
   }
 
-  if (iomgr_success && chand->resolver) {
-    set_channel_connectivity_state_locked(exec_ctx, chand, state,
-                                          "new_lb+resolver");
+  if (error == GRPC_ERROR_NONE && chand->resolver) {
+    set_channel_connectivity_state_locked(
+        exec_ctx, chand, state, GRPC_ERROR_REF(state_error), "new_lb+resolver");
     if (lb_policy != NULL) {
       watch_lb_policy(exec_ctx, chand, lb_policy, state);
     }
@@ -236,8 +244,12 @@
       GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel");
       chand->resolver = NULL;
     }
+    grpc_error *refs[] = {error, state_error};
     set_channel_connectivity_state_locked(
-        exec_ctx, chand, GRPC_CHANNEL_FATAL_FAILURE, "resolver_gone");
+        exec_ctx, chand, GRPC_CHANNEL_FATAL_FAILURE,
+        GRPC_ERROR_CREATE_REFERENCING("Got config after disconnection", refs,
+                                      GPR_ARRAY_SIZE(refs)),
+        "resolver_gone");
     gpr_mu_unlock(&chand->mu_config);
   }
 
@@ -257,6 +269,7 @@
   }
 
   GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->owning_stack, "resolver");
+  GRPC_ERROR_UNREF(state_error);
 }
 
 static void cc_start_transport_op(grpc_exec_ctx *exec_ctx,
@@ -264,7 +277,7 @@
                                   grpc_transport_op *op) {
   channel_data *chand = elem->channel_data;
 
-  grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, true, NULL);
+  grpc_exec_ctx_push(exec_ctx, op->on_consumed, GRPC_ERROR_NONE, NULL);
 
   GPR_ASSERT(op->set_accept_stream == false);
   if (op->bind_pollset != NULL) {
@@ -283,7 +296,9 @@
 
   if (op->send_ping != NULL) {
     if (chand->lb_policy == NULL) {
-      grpc_exec_ctx_enqueue(exec_ctx, op->send_ping, false, NULL);
+      grpc_exec_ctx_push(exec_ctx, op->send_ping,
+                         GRPC_ERROR_CREATE("Ping with no load balancing"),
+                         NULL);
     } else {
       grpc_lb_policy_ping_one(exec_ctx, chand->lb_policy, op->send_ping);
       op->bind_pollset = NULL;
@@ -291,24 +306,29 @@
     op->send_ping = NULL;
   }
 
-  if (op->disconnect && chand->resolver != NULL) {
-    set_channel_connectivity_state_locked(
-        exec_ctx, chand, GRPC_CHANNEL_FATAL_FAILURE, "disconnect");
-    grpc_resolver_shutdown(exec_ctx, chand->resolver);
-    GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel");
-    chand->resolver = NULL;
-    if (!chand->started_resolving) {
-      grpc_closure_list_fail_all(&chand->waiting_for_config_closures);
-      grpc_exec_ctx_enqueue_list(exec_ctx, &chand->waiting_for_config_closures,
-                                 NULL);
+  if (op->disconnect_with_error != GRPC_ERROR_NONE) {
+    if (chand->resolver != NULL) {
+      set_channel_connectivity_state_locked(
+          exec_ctx, chand, GRPC_CHANNEL_FATAL_FAILURE,
+          GRPC_ERROR_REF(op->disconnect_with_error), "disconnect");
+      grpc_resolver_shutdown(exec_ctx, chand->resolver);
+      GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel");
+      chand->resolver = NULL;
+      if (!chand->started_resolving) {
+        grpc_closure_list_fail_all(&chand->waiting_for_config_closures,
+                                   GRPC_ERROR_REF(op->disconnect_with_error));
+        grpc_exec_ctx_enqueue_list(exec_ctx,
+                                   &chand->waiting_for_config_closures, NULL);
+      }
+      if (chand->lb_policy != NULL) {
+        grpc_pollset_set_del_pollset_set(exec_ctx,
+                                         chand->lb_policy->interested_parties,
+                                         chand->interested_parties);
+        GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel");
+        chand->lb_policy = NULL;
+      }
     }
-    if (chand->lb_policy != NULL) {
-      grpc_pollset_set_del_pollset_set(exec_ctx,
-                                       chand->lb_policy->interested_parties,
-                                       chand->interested_parties);
-      GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel");
-      chand->lb_policy = NULL;
-    }
+    GRPC_ERROR_UNREF(op->disconnect_with_error);
   }
   gpr_mu_unlock(&chand->mu_config);
 }
@@ -328,16 +348,17 @@
                               grpc_connected_subchannel **connected_subchannel,
                               grpc_closure *on_ready);
 
-static void continue_picking(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void continue_picking(grpc_exec_ctx *exec_ctx, void *arg,
+                             grpc_error *error) {
   continue_picking_args *cpa = arg;
   if (cpa->connected_subchannel == NULL) {
     /* cancelled, do nothing */
-  } else if (!success) {
-    grpc_exec_ctx_enqueue(exec_ctx, cpa->on_ready, false, NULL);
+  } else if (error != GRPC_ERROR_NONE) {
+    grpc_exec_ctx_push(exec_ctx, cpa->on_ready, GRPC_ERROR_REF(error), NULL);
   } else if (cc_pick_subchannel(exec_ctx, cpa->elem, cpa->initial_metadata,
                                 cpa->initial_metadata_flags,
                                 cpa->connected_subchannel, cpa->on_ready)) {
-    grpc_exec_ctx_enqueue(exec_ctx, cpa->on_ready, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, cpa->on_ready, GRPC_ERROR_NONE, NULL);
   }
   gpr_free(cpa);
 }
@@ -362,11 +383,12 @@
                                  connected_subchannel);
     }
     for (closure = chand->waiting_for_config_closures.head; closure != NULL;
-         closure = grpc_closure_next(closure)) {
+         closure = closure->next_data.next) {
       cpa = closure->cb_arg;
       if (cpa->connected_subchannel == connected_subchannel) {
         cpa->connected_subchannel = NULL;
-        grpc_exec_ctx_enqueue(exec_ctx, cpa->on_ready, false, NULL);
+        grpc_exec_ctx_push(exec_ctx, cpa->on_ready,
+                           GRPC_ERROR_CREATE("Pick cancelled"), NULL);
       }
     }
     gpr_mu_unlock(&chand->mu_config);
@@ -398,10 +420,11 @@
     cpa->on_ready = on_ready;
     cpa->elem = elem;
     grpc_closure_init(&cpa->closure, continue_picking, cpa);
-    grpc_closure_list_add(&chand->waiting_for_config_closures, &cpa->closure,
-                          1);
+    grpc_closure_list_append(&chand->waiting_for_config_closures, &cpa->closure,
+                             GRPC_ERROR_NONE);
   } else {
-    grpc_exec_ctx_enqueue(exec_ctx, on_ready, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, on_ready, GRPC_ERROR_CREATE("Disconnected"),
+                       NULL);
   }
   gpr_mu_unlock(&chand->mu_config);
   return 0;
@@ -506,7 +529,7 @@
   channel_data *chand = elem->channel_data;
   grpc_connectivity_state out;
   gpr_mu_lock(&chand->mu_config);
-  out = grpc_connectivity_state_check(&chand->state_tracker);
+  out = grpc_connectivity_state_check(&chand->state_tracker, NULL);
   if (out == GRPC_CHANNEL_IDLE && try_to_connect) {
     if (chand->lb_policy != NULL) {
       grpc_lb_policy_exit_idle(exec_ctx, chand->lb_policy);
@@ -533,7 +556,7 @@
 } external_connectivity_watcher;
 
 static void on_external_watch_complete(grpc_exec_ctx *exec_ctx, void *arg,
-                                       bool iomgr_success) {
+                                       grpc_error *error) {
   external_connectivity_watcher *w = arg;
   grpc_closure *follow_up = w->on_complete;
   grpc_pollset_set_del_pollset(exec_ctx, w->chand->interested_parties,
@@ -541,7 +564,7 @@
   GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack,
                            "external_connectivity_watcher");
   gpr_free(w);
-  follow_up->cb(exec_ctx, follow_up->cb_arg, iomgr_success);
+  follow_up->cb(exec_ctx, follow_up->cb_arg, error);
 }
 
 void grpc_client_channel_watch_connectivity_state(
diff --git a/src/core/ext/client_config/lb_policy.c b/src/core/ext/client_config/lb_policy.c
index a7ad984..fcff0c9 100644
--- a/src/core/ext/client_config/lb_policy.c
+++ b/src/core/ext/client_config/lb_policy.c
@@ -138,6 +138,8 @@
 }
 
 grpc_connectivity_state grpc_lb_policy_check_connectivity(
-    grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) {
-  return policy->vtable->check_connectivity(exec_ctx, policy);
+    grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
+    grpc_error **connectivity_error) {
+  return policy->vtable->check_connectivity(exec_ctx, policy,
+                                            connectivity_error);
 }
diff --git a/src/core/ext/client_config/lb_policy.h b/src/core/ext/client_config/lb_policy.h
index 0384e0b..13b9abc 100644
--- a/src/core/ext/client_config/lb_policy.h
+++ b/src/core/ext/client_config/lb_policy.h
@@ -75,8 +75,9 @@
   void (*exit_idle)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy);
 
   /** check the current connectivity of the lb_policy */
-  grpc_connectivity_state (*check_connectivity)(grpc_exec_ctx *exec_ctx,
-                                                grpc_lb_policy *policy);
+  grpc_connectivity_state (*check_connectivity)(
+      grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
+      grpc_error **connectivity_error);
 
   /** call notify when the connectivity state of a channel changes from *state.
       Updates *state with the new state of the policy */
@@ -152,6 +153,7 @@
                                            grpc_closure *closure);
 
 grpc_connectivity_state grpc_lb_policy_check_connectivity(
-    grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy);
+    grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy,
+    grpc_error **connectivity_error);
 
 #endif /* GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_H */
diff --git a/src/core/ext/client_config/subchannel.c b/src/core/ext/client_config/subchannel.c
index bd45d38..0c175cd 100644
--- a/src/core/ext/client_config/subchannel.c
+++ b/src/core/ext/client_config/subchannel.c
@@ -54,7 +54,7 @@
 #define STRONG_REF_MASK (~(gpr_atm)((1 << INTERNAL_REF_BITS) - 1))
 
 #define GRPC_SUBCHANNEL_MIN_CONNECT_TIMEOUT_SECONDS 20
-#define GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS 2
+#define GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS 1
 #define GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER 1.6
 #define GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS 120
 #define GRPC_SUBCHANNEL_RECONNECT_JITTER 0.2
@@ -147,7 +147,7 @@
   (((grpc_subchannel_call *)(callstack)) - 1)
 
 static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *subchannel,
-                                 bool iomgr_success);
+                                 grpc_error *error);
 
 #ifdef GRPC_STREAM_REFCOUNT_DEBUG
 #define REF_REASON reason
@@ -177,7 +177,7 @@
  */
 
 static void connection_destroy(grpc_exec_ctx *exec_ctx, void *arg,
-                               bool success) {
+                               grpc_error *error) {
   grpc_connected_subchannel *c = arg;
   grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c));
   gpr_free(c);
@@ -200,7 +200,7 @@
  */
 
 static void subchannel_destroy(grpc_exec_ctx *exec_ctx, void *arg,
-                               bool success) {
+                               grpc_error *error) {
   grpc_subchannel *c = arg;
   gpr_free((void *)c->filters);
   grpc_channel_args_destroy(c->args);
@@ -290,8 +290,8 @@
   gpr_atm old_refs;
   old_refs = ref_mutate(c, -(gpr_atm)1, 1 REF_MUTATE_PURPOSE("WEAK_UNREF"));
   if (old_refs == 1) {
-    grpc_exec_ctx_enqueue(exec_ctx, grpc_closure_create(subchannel_destroy, c),
-                          true, NULL);
+    grpc_exec_ctx_push(exec_ctx, grpc_closure_create(subchannel_destroy, c),
+                       GRPC_ERROR_NONE, NULL);
   }
 }
 
@@ -382,7 +382,8 @@
   args.initial_connect_string = c->initial_connect_string;
 
   grpc_connectivity_state_set(exec_ctx, &c->state_tracker,
-                              GRPC_CHANNEL_CONNECTING, "state_change");
+                              GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE,
+                              "state_change");
   grpc_connector_connect(exec_ctx, c->connector, &args, &c->connecting_result,
                          &c->connected);
 }
@@ -393,16 +394,17 @@
   continue_connect(exec_ctx, c);
 }
 
-grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c) {
+grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c,
+                                                           grpc_error **error) {
   grpc_connectivity_state state;
   gpr_mu_lock(&c->mu);
-  state = grpc_connectivity_state_check(&c->state_tracker);
+  state = grpc_connectivity_state_check(&c->state_tracker, error);
   gpr_mu_unlock(&c->mu);
   return state;
 }
 
 static void on_external_state_watcher_done(grpc_exec_ctx *exec_ctx, void *arg,
-                                           bool success) {
+                                           grpc_error *error) {
   external_state_watcher *w = arg;
   grpc_closure *follow_up = w->notify;
   if (w->pollset_set != NULL) {
@@ -415,7 +417,7 @@
   gpr_mu_unlock(&w->subchannel->mu);
   GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, w->subchannel, "external_state_watcher");
   gpr_free(w);
-  follow_up->cb(exec_ctx, follow_up->cb_arg, success);
+  follow_up->cb(exec_ctx, follow_up->cb_arg, error);
 }
 
 void grpc_subchannel_notify_on_state_change(
@@ -469,7 +471,7 @@
 }
 
 static void subchannel_on_child_state_changed(grpc_exec_ctx *exec_ctx, void *p,
-                                              bool iomgr_success) {
+                                              grpc_error *error) {
   state_watcher *sw = p;
   grpc_subchannel *c = sw->subchannel;
   gpr_mu *mu = &c->mu;
@@ -477,20 +479,19 @@
   gpr_mu_lock(mu);
 
   /* if we failed just leave this closure */
-  if (iomgr_success) {
-    if (sw->connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
-      /* any errors on a subchannel ==> we're done, create a new one */
-      sw->connectivity_state = GRPC_CHANNEL_FATAL_FAILURE;
-    }
-    grpc_connectivity_state_set(exec_ctx, &c->state_tracker,
-                                sw->connectivity_state, "reflect_child");
-    if (sw->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE) {
-      grpc_connected_subchannel_notify_on_state_change(
-          exec_ctx, GET_CONNECTED_SUBCHANNEL(c, no_barrier), NULL,
-          &sw->connectivity_state, &sw->closure);
-      GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher");
-      sw = NULL;
-    }
+  if (sw->connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) {
+    /* any errors on a subchannel ==> we're done, create a new one */
+    sw->connectivity_state = GRPC_CHANNEL_FATAL_FAILURE;
+  }
+  grpc_connectivity_state_set(exec_ctx, &c->state_tracker,
+                              sw->connectivity_state, GRPC_ERROR_REF(error),
+                              "reflect_child");
+  if (sw->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE) {
+    grpc_connected_subchannel_notify_on_state_change(
+        exec_ctx, GET_CONNECTED_SUBCHANNEL(c, no_barrier), NULL,
+        &sw->connectivity_state, &sw->closure);
+    GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher");
+    sw = NULL;
   }
 
   gpr_mu_unlock(mu);
@@ -592,17 +593,20 @@
 
   /* signal completion */
   grpc_connectivity_state_set(exec_ctx, &c->state_tracker, GRPC_CHANNEL_READY,
-                              "connected");
+                              GRPC_ERROR_NONE, "connected");
 }
 
-static void on_alarm(grpc_exec_ctx *exec_ctx, void *arg, bool iomgr_success) {
+static void on_alarm(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   grpc_subchannel *c = arg;
   gpr_mu_lock(&c->mu);
   c->have_alarm = 0;
   if (c->disconnected) {
-    iomgr_success = 0;
+    error = GRPC_ERROR_CREATE_REFERENCING("Disconnected", &error, 1);
+  } else {
+    GRPC_ERROR_REF(error);
   }
-  if (iomgr_success) {
+  if (error == GRPC_ERROR_NONE) {
+    gpr_log(GPR_INFO, "Failed to connect to channel, retrying");
     c->next_attempt =
         gpr_backoff_step(&c->backoff_state, gpr_now(GPR_CLOCK_MONOTONIC));
     continue_connect(exec_ctx, c);
@@ -611,10 +615,11 @@
     gpr_mu_unlock(&c->mu);
     GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting");
   }
+  GRPC_ERROR_UNREF(error);
 }
 
 static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *arg,
-                                 bool iomgr_success) {
+                                 grpc_error *error) {
   grpc_subchannel *c = arg;
 
   GRPC_SUBCHANNEL_WEAK_REF(c, "connected");
@@ -627,9 +632,15 @@
     gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
     GPR_ASSERT(!c->have_alarm);
     c->have_alarm = 1;
-    grpc_connectivity_state_set(exec_ctx, &c->state_tracker,
-                                GRPC_CHANNEL_TRANSIENT_FAILURE,
-                                "connect_failed");
+    grpc_connectivity_state_set(
+        exec_ctx, &c->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+        GRPC_ERROR_CREATE_REFERENCING("Connect Failed", &error, 1),
+        "connect_failed");
+    gpr_timespec time_til_next = gpr_time_sub(c->next_attempt, now);
+    const char *errmsg = grpc_error_string(error);
+    gpr_log(GPR_INFO, "Connect failed, retry in %d.%09d seconds: %s",
+            time_til_next.tv_sec, time_til_next.tv_nsec, errmsg);
+    grpc_error_free_string(errmsg);
     grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, on_alarm, c, now);
   }
   gpr_mu_unlock(&c->mu);
@@ -641,7 +652,7 @@
  */
 
 static void subchannel_call_destroy(grpc_exec_ctx *exec_ctx, void *call,
-                                    bool success) {
+                                    grpc_error *error) {
   grpc_subchannel_call *c = call;
   GPR_TIMER_BEGIN("grpc_subchannel_call_unref.destroy", 0);
   grpc_connected_subchannel *connection = c->connection;
diff --git a/src/core/ext/client_config/subchannel.h b/src/core/ext/client_config/subchannel.h
index 0765a54..e73f394 100644
--- a/src/core/ext/client_config/subchannel.h
+++ b/src/core/ext/client_config/subchannel.h
@@ -118,7 +118,7 @@
 
 /** poll the current connectivity state of a channel */
 grpc_connectivity_state grpc_subchannel_check_connectivity(
-    grpc_subchannel *channel);
+    grpc_subchannel *channel, grpc_error **error);
 
 /** call notify when the connectivity state of a channel changes from *state.
     Updates *state with the new state of the channel */
diff --git a/src/core/ext/client_config/subchannel_call_holder.c b/src/core/ext/client_config/subchannel_call_holder.c
index 91fa917..07b7813 100644
--- a/src/core/ext/client_config/subchannel_call_holder.c
+++ b/src/core/ext/client_config/subchannel_call_holder.c
@@ -43,14 +43,14 @@
 #define CANCELLED_CALL ((grpc_subchannel_call *)1)
 
 static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *holder,
-                             bool success);
+                             grpc_error *error);
 static void retry_ops(grpc_exec_ctx *exec_ctx, void *retry_ops_args,
-                      bool success);
+                      grpc_error *error);
 
 static void add_waiting_locked(grpc_subchannel_call_holder *holder,
                                grpc_transport_stream_op *op);
 static void fail_locked(grpc_exec_ctx *exec_ctx,
-                        grpc_subchannel_call_holder *holder);
+                        grpc_subchannel_call_holder *holder, grpc_error *error);
 static void retry_waiting_locked(grpc_exec_ctx *exec_ctx,
                                  grpc_subchannel_call_holder *holder);
 
@@ -90,7 +90,8 @@
   grpc_subchannel_call *call = GET_CALL(holder);
   GPR_TIMER_BEGIN("grpc_subchannel_call_holder_perform_op", 0);
   if (call == CANCELLED_CALL) {
-    grpc_transport_stream_op_finish_with_failure(exec_ctx, op);
+    grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
+                                                 GRPC_ERROR_CANCELLED);
     GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0);
     return;
   }
@@ -106,7 +107,8 @@
   call = GET_CALL(holder);
   if (call == CANCELLED_CALL) {
     gpr_mu_unlock(&holder->mu);
-    grpc_transport_stream_op_finish_with_failure(exec_ctx, op);
+    grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
+                                                 GRPC_ERROR_CANCELLED);
     GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0);
     return;
   }
@@ -123,7 +125,10 @@
     } else {
       switch (holder->creation_phase) {
         case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING:
-          fail_locked(exec_ctx, holder);
+          fail_locked(exec_ctx, holder,
+                      grpc_error_set_int(GRPC_ERROR_CREATE("Cancelled"),
+                                         GRPC_ERROR_INT_GRPC_STATUS,
+                                         op->cancel_with_status));
           break;
         case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL:
           holder->pick_subchannel(exec_ctx, holder->pick_subchannel_arg, NULL,
@@ -131,7 +136,8 @@
           break;
       }
       gpr_mu_unlock(&holder->mu);
-      grpc_transport_stream_op_finish_with_failure(exec_ctx, op);
+      grpc_transport_stream_op_finish_with_failure(exec_ctx, op,
+                                                   GRPC_ERROR_CANCELLED);
       GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0);
       return;
     }
@@ -167,7 +173,8 @@
   GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0);
 }
 
-static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg,
+                             grpc_error *error) {
   grpc_subchannel_call_holder *holder = arg;
   gpr_mu_lock(&holder->mu);
   GPR_ASSERT(holder->creation_phase ==
@@ -175,10 +182,14 @@
   holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
   if (holder->connected_subchannel == NULL) {
     gpr_atm_no_barrier_store(&holder->subchannel_call, 1);
-    fail_locked(exec_ctx, holder);
+    fail_locked(exec_ctx, holder,
+                GRPC_ERROR_CREATE_REFERENCING("Failed to create subchannel",
+                                              &error, 1));
   } else if (1 == gpr_atm_acq_load(&holder->subchannel_call)) {
     /* already cancelled before subchannel became ready */
-    fail_locked(exec_ctx, holder);
+    fail_locked(exec_ctx, holder,
+                GRPC_ERROR_CREATE_REFERENCING(
+                    "Cancelled before creating subchannel", &error, 1));
   } else {
     gpr_atm_rel_store(
         &holder->subchannel_call,
@@ -204,18 +215,18 @@
   a->call = GET_CALL(holder);
   if (a->call == CANCELLED_CALL) {
     gpr_free(a);
-    fail_locked(exec_ctx, holder);
+    fail_locked(exec_ctx, holder, GRPC_ERROR_CANCELLED);
     return;
   }
   holder->waiting_ops = NULL;
   holder->waiting_ops_count = 0;
   holder->waiting_ops_capacity = 0;
   GRPC_SUBCHANNEL_CALL_REF(a->call, "retry_ops");
-  grpc_exec_ctx_enqueue(exec_ctx, grpc_closure_create(retry_ops, a), true,
-                        NULL);
+  grpc_exec_ctx_push(exec_ctx, grpc_closure_create(retry_ops, a),
+                     GRPC_ERROR_NONE, NULL);
 }
 
-static void retry_ops(grpc_exec_ctx *exec_ctx, void *args, bool success) {
+static void retry_ops(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) {
   retry_ops_args *a = args;
   size_t i;
   for (i = 0; i < a->nops; i++) {
@@ -240,13 +251,15 @@
 }
 
 static void fail_locked(grpc_exec_ctx *exec_ctx,
-                        grpc_subchannel_call_holder *holder) {
+                        grpc_subchannel_call_holder *holder,
+                        grpc_error *error) {
   size_t i;
   for (i = 0; i < holder->waiting_ops_count; i++) {
-    grpc_transport_stream_op_finish_with_failure(exec_ctx,
-                                                 &holder->waiting_ops[i]);
+    grpc_transport_stream_op_finish_with_failure(
+        exec_ctx, &holder->waiting_ops[i], GRPC_ERROR_REF(error));
   }
   holder->waiting_ops_count = 0;
+  GRPC_ERROR_UNREF(error);
 }
 
 char *grpc_subchannel_call_holder_get_peer(
diff --git a/src/core/ext/lb_policy/pick_first/pick_first.c b/src/core/ext/lb_policy/pick_first/pick_first.c
index 0d215cd..b5c2c42 100644
--- a/src/core/ext/lb_policy/pick_first/pick_first.c
+++ b/src/core/ext/lb_policy/pick_first/pick_first.c
@@ -103,8 +103,9 @@
   p->shutdown = 1;
   pp = p->pending_picks;
   p->pending_picks = NULL;
-  grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                              GRPC_CHANNEL_FATAL_FAILURE, "shutdown");
+  grpc_connectivity_state_set(
+      exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE,
+      GRPC_ERROR_CREATE("Channel shutdown"), "shutdown");
   /* cancel subscription */
   if (selected != NULL) {
     grpc_connected_subchannel_notify_on_state_change(
@@ -120,7 +121,7 @@
     *pp->target = NULL;
     grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
                                  pp->pollset);
-    grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, pp->on_complete, GRPC_ERROR_NONE, NULL);
     gpr_free(pp);
     pp = next;
   }
@@ -139,7 +140,8 @@
       grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
                                    pp->pollset);
       *target = NULL;
-      grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
+      grpc_exec_ctx_push(exec_ctx, pp->on_complete,
+                         GRPC_ERROR_CREATE("Pick Cancelled"), NULL);
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -164,7 +166,8 @@
         initial_metadata_flags_eq) {
       grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
                                    pp->pollset);
-      grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
+      grpc_exec_ctx_push(exec_ctx, pp->on_complete,
+                         GRPC_ERROR_CREATE("Pick Cancelled"), NULL);
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -235,7 +238,7 @@
 }
 
 static void destroy_subchannels(grpc_exec_ctx *exec_ctx, void *arg,
-                                bool iomgr_success) {
+                                grpc_error *error) {
   pick_first_lb_policy *p = arg;
   size_t i;
   size_t num_subchannels = p->num_subchannels;
@@ -256,12 +259,14 @@
 }
 
 static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
-                                    bool iomgr_success) {
+                                    grpc_error *error) {
   pick_first_lb_policy *p = arg;
   grpc_subchannel *selected_subchannel;
   pending_pick *pp;
   grpc_connected_subchannel *selected;
 
+  GRPC_ERROR_REF(error);
+
   gpr_mu_lock(&p->mu);
 
   selected = GET_SELECTED(p);
@@ -269,6 +274,7 @@
   if (p->shutdown) {
     gpr_mu_unlock(&p->mu);
     GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity");
+    GRPC_ERROR_UNREF(error);
     return;
   } else if (selected != NULL) {
     if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) {
@@ -276,7 +282,8 @@
       p->checking_connectivity = GRPC_CHANNEL_FATAL_FAILURE;
     }
     grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                p->checking_connectivity, "selected_changed");
+                                p->checking_connectivity, GRPC_ERROR_REF(error),
+                                "selected_changed");
     if (p->checking_connectivity != GRPC_CHANNEL_FATAL_FAILURE) {
       grpc_connected_subchannel_notify_on_state_change(
           exec_ctx, selected, p->base.interested_parties,
@@ -289,7 +296,8 @@
     switch (p->checking_connectivity) {
       case GRPC_CHANNEL_READY:
         grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                    GRPC_CHANNEL_READY, "connecting_ready");
+                                    GRPC_CHANNEL_READY, GRPC_ERROR_NONE,
+                                    "connecting_ready");
         selected_subchannel = p->subchannels[p->checking_subchannel];
         selected =
             grpc_subchannel_get_connected_subchannel(selected_subchannel);
@@ -298,15 +306,16 @@
         /* drop the pick list: we are connected now */
         GRPC_LB_POLICY_WEAK_REF(&p->base, "destroy_subchannels");
         gpr_atm_rel_store(&p->selected, (gpr_atm)selected);
-        grpc_exec_ctx_enqueue(
-            exec_ctx, grpc_closure_create(destroy_subchannels, p), true, NULL);
+        grpc_exec_ctx_push(exec_ctx,
+                           grpc_closure_create(destroy_subchannels, p),
+                           GRPC_ERROR_NONE, NULL);
         /* update any calls that were waiting for a pick */
         while ((pp = p->pending_picks)) {
           p->pending_picks = pp->next;
           *pp->target = selected;
           grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
                                        pp->pollset);
-          grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
+          grpc_exec_ctx_push(exec_ctx, pp->on_complete, GRPC_ERROR_NONE, NULL);
           gpr_free(pp);
         }
         grpc_connected_subchannel_notify_on_state_change(
@@ -318,12 +327,13 @@
             (p->checking_subchannel + 1) % p->num_subchannels;
         if (p->checking_subchannel == 0) {
           /* only trigger transient failure when we've tried all alternatives */
-          grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                      GRPC_CHANNEL_TRANSIENT_FAILURE,
-                                      "connecting_transient_failure");
+          grpc_connectivity_state_set(
+              exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+              GRPC_ERROR_REF(error), "connecting_transient_failure");
         }
+        GRPC_ERROR_UNREF(error);
         p->checking_connectivity = grpc_subchannel_check_connectivity(
-            p->subchannels[p->checking_subchannel]);
+            p->subchannels[p->checking_subchannel], &error);
         if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) {
           grpc_subchannel_notify_on_state_change(
               exec_ctx, p->subchannels[p->checking_subchannel],
@@ -335,9 +345,9 @@
         break;
       case GRPC_CHANNEL_CONNECTING:
       case GRPC_CHANNEL_IDLE:
-        grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                    GRPC_CHANNEL_CONNECTING,
-                                    "connecting_changed");
+        grpc_connectivity_state_set(
+            exec_ctx, &p->state_tracker, GRPC_CHANNEL_CONNECTING,
+            GRPC_ERROR_REF(error), "connecting_changed");
         grpc_subchannel_notify_on_state_change(
             exec_ctx, p->subchannels[p->checking_subchannel],
             p->base.interested_parties, &p->checking_connectivity,
@@ -350,38 +360,45 @@
         GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[p->num_subchannels],
                               "pick_first");
         if (p->num_subchannels == 0) {
-          grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                      GRPC_CHANNEL_FATAL_FAILURE,
-                                      "no_more_channels");
+          grpc_connectivity_state_set(
+              exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE,
+              GRPC_ERROR_CREATE_REFERENCING("Pick first exhausted channels",
+                                            &error, 1),
+              "no_more_channels");
           while ((pp = p->pending_picks)) {
             p->pending_picks = pp->next;
             *pp->target = NULL;
-            grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
+            grpc_exec_ctx_push(exec_ctx, pp->on_complete, GRPC_ERROR_NONE,
+                               NULL);
             gpr_free(pp);
           }
           GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base,
                                     "pick_first_connectivity");
         } else {
-          grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                      GRPC_CHANNEL_TRANSIENT_FAILURE,
-                                      "subchannel_failed");
+          grpc_connectivity_state_set(
+              exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+              GRPC_ERROR_REF(error), "subchannel_failed");
           p->checking_subchannel %= p->num_subchannels;
+          GRPC_ERROR_UNREF(error);
           p->checking_connectivity = grpc_subchannel_check_connectivity(
-              p->subchannels[p->checking_subchannel]);
+              p->subchannels[p->checking_subchannel], &error);
           goto loop;
         }
     }
   }
 
   gpr_mu_unlock(&p->mu);
+
+  GRPC_ERROR_UNREF(error);
 }
 
 static grpc_connectivity_state pf_check_connectivity(grpc_exec_ctx *exec_ctx,
-                                                     grpc_lb_policy *pol) {
+                                                     grpc_lb_policy *pol,
+                                                     grpc_error **error) {
   pick_first_lb_policy *p = (pick_first_lb_policy *)pol;
   grpc_connectivity_state st;
   gpr_mu_lock(&p->mu);
-  st = grpc_connectivity_state_check(&p->state_tracker);
+  st = grpc_connectivity_state_check(&p->state_tracker, error);
   gpr_mu_unlock(&p->mu);
   return st;
 }
@@ -404,7 +421,8 @@
   if (selected) {
     grpc_connected_subchannel_ping(exec_ctx, selected, closure);
   } else {
-    grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, closure, GRPC_ERROR_CREATE("Not connected"),
+                       NULL);
   }
 }
 
diff --git a/src/core/ext/lb_policy/round_robin/round_robin.c b/src/core/ext/lb_policy/round_robin/round_robin.c
index dcdc0c6..95305e9 100644
--- a/src/core/ext/lb_policy/round_robin/round_robin.c
+++ b/src/core/ext/lb_policy/round_robin/round_robin.c
@@ -239,11 +239,13 @@
   while ((pp = p->pending_picks)) {
     p->pending_picks = pp->next;
     *pp->target = NULL;
-    grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, pp->on_complete,
+                       GRPC_ERROR_CREATE("Channel Shutdown"), NULL);
     gpr_free(pp);
   }
-  grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                              GRPC_CHANNEL_FATAL_FAILURE, "shutdown");
+  grpc_connectivity_state_set(
+      exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE,
+      GRPC_ERROR_CREATE("Channel Shutdown"), "shutdown");
   for (i = 0; i < p->num_subchannels; i++) {
     subchannel_data *sd = p->subchannels[i];
     grpc_subchannel_notify_on_state_change(exec_ctx, sd->subchannel, NULL, NULL,
@@ -265,7 +267,7 @@
       grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
                                    pp->pollset);
       *target = NULL;
-      grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
+      grpc_exec_ctx_push(exec_ctx, pp->on_complete, GRPC_ERROR_CANCELLED, NULL);
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -291,7 +293,7 @@
       grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
                                    pp->pollset);
       *pp->target = NULL;
-      grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL);
+      grpc_exec_ctx_push(exec_ctx, pp->on_complete, GRPC_ERROR_CANCELLED, NULL);
       gpr_free(pp);
     } else {
       pp->next = p->pending_picks;
@@ -368,7 +370,7 @@
 }
 
 static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg,
-                                    bool iomgr_success) {
+                                    grpc_error *error) {
   subchannel_data *sd = arg;
   round_robin_lb_policy *p = sd->policy;
   pending_pick *pp;
@@ -376,6 +378,7 @@
 
   int unref = 0;
 
+  GRPC_ERROR_REF(error);
   gpr_mu_lock(&p->mu);
 
   if (p->shutdown) {
@@ -384,7 +387,8 @@
     switch (sd->connectivity_state) {
       case GRPC_CHANNEL_READY:
         grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                    GRPC_CHANNEL_READY, "connecting_ready");
+                                    GRPC_CHANNEL_READY, GRPC_ERROR_REF(error),
+                                    "connecting_ready");
         /* add the newly connected subchannel to the list of connected ones.
          * Note that it goes to the "end of the line". */
         sd->ready_list_node = add_connected_sc_locked(p, sd->subchannel);
@@ -408,7 +412,7 @@
           }
           grpc_pollset_set_del_pollset(exec_ctx, p->base.interested_parties,
                                        pp->pollset);
-          grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
+          grpc_exec_ctx_push(exec_ctx, pp->on_complete, GRPC_ERROR_NONE, NULL);
           gpr_free(pp);
         }
         grpc_subchannel_notify_on_state_change(
@@ -417,9 +421,9 @@
         break;
       case GRPC_CHANNEL_CONNECTING:
       case GRPC_CHANNEL_IDLE:
-        grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                    sd->connectivity_state,
-                                    "connecting_changed");
+        grpc_connectivity_state_set(
+            exec_ctx, &p->state_tracker, sd->connectivity_state,
+            GRPC_ERROR_REF(error), "connecting_changed");
         grpc_subchannel_notify_on_state_change(
             exec_ctx, sd->subchannel, p->base.interested_parties,
             &sd->connectivity_state, &sd->connectivity_changed_closure);
@@ -435,9 +439,9 @@
           remove_disconnected_sc_locked(p, sd->ready_list_node);
           sd->ready_list_node = NULL;
         }
-        grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                    GRPC_CHANNEL_TRANSIENT_FAILURE,
-                                    "connecting_transient_failure");
+        grpc_connectivity_state_set(
+            exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+            GRPC_ERROR_REF(error), "connecting_transient_failure");
         break;
       case GRPC_CHANNEL_FATAL_FAILURE:
         if (sd->ready_list_node != NULL) {
@@ -454,19 +458,22 @@
 
         unref = 1;
         if (p->num_subchannels == 0) {
-          grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                      GRPC_CHANNEL_FATAL_FAILURE,
-                                      "no_more_channels");
+          grpc_connectivity_state_set(
+              exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE,
+              GRPC_ERROR_CREATE_REFERENCING("Round Robin Channels Exhausted",
+                                            &error, 1),
+              "no_more_channels");
           while ((pp = p->pending_picks)) {
             p->pending_picks = pp->next;
             *pp->target = NULL;
-            grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL);
+            grpc_exec_ctx_push(exec_ctx, pp->on_complete, GRPC_ERROR_NONE,
+                               NULL);
             gpr_free(pp);
           }
         } else {
-          grpc_connectivity_state_set(exec_ctx, &p->state_tracker,
-                                      GRPC_CHANNEL_TRANSIENT_FAILURE,
-                                      "subchannel_failed");
+          grpc_connectivity_state_set(
+              exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE,
+              GRPC_ERROR_REF(error), "subchannel_failed");
         }
     } /* switch */
   }   /* !unref */
@@ -476,14 +483,17 @@
   if (unref) {
     GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "round_robin_connectivity");
   }
+
+  GRPC_ERROR_UNREF(error);
 }
 
 static grpc_connectivity_state rr_check_connectivity(grpc_exec_ctx *exec_ctx,
-                                                     grpc_lb_policy *pol) {
+                                                     grpc_lb_policy *pol,
+                                                     grpc_error **error) {
   round_robin_lb_policy *p = (round_robin_lb_policy *)pol;
   grpc_connectivity_state st;
   gpr_mu_lock(&p->mu);
-  st = grpc_connectivity_state_check(&p->state_tracker);
+  st = grpc_connectivity_state_check(&p->state_tracker, error);
   gpr_mu_unlock(&p->mu);
   return st;
 }
@@ -511,7 +521,8 @@
     grpc_connected_subchannel_ping(exec_ctx, target, closure);
   } else {
     gpr_mu_unlock(&p->mu);
-    grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, closure,
+                       GRPC_ERROR_CREATE("Round Robin not connected"), NULL);
   }
 }
 
diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c
index 620ba4e..adb9c3a 100644
--- a/src/core/ext/resolver/dns/native/dns_resolver.c
+++ b/src/core/ext/resolver/dns/native/dns_resolver.c
@@ -82,6 +82,9 @@
   grpc_timer retry_timer;
   /** retry backoff state */
   gpr_backoff backoff_state;
+
+  /** currently resolving addresses */
+  grpc_resolved_addresses *addresses;
 } dns_resolver;
 
 static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r);
@@ -108,7 +111,8 @@
   }
   if (r->next_completion != NULL) {
     *r->target_config = NULL;
-    grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, r->next_completion,
+                       GRPC_ERROR_CREATE("Resolver Shutdown"), NULL);
     r->next_completion = NULL;
   }
   gpr_mu_unlock(&r->mu);
@@ -143,12 +147,12 @@
 }
 
 static void dns_on_retry_timer(grpc_exec_ctx *exec_ctx, void *arg,
-                               bool success) {
+                               grpc_error *error) {
   dns_resolver *r = arg;
 
   gpr_mu_lock(&r->mu);
   r->have_retry_timer = false;
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     if (!r->resolving) {
       dns_start_resolving_locked(exec_ctx, r);
     }
@@ -159,13 +163,14 @@
 }
 
 static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
-                            grpc_resolved_addresses *addresses) {
+                            grpc_error *error) {
   dns_resolver *r = arg;
   grpc_client_config *config = NULL;
   grpc_lb_policy *lb_policy;
   gpr_mu_lock(&r->mu);
   GPR_ASSERT(r->resolving);
   r->resolving = 0;
+  grpc_resolved_addresses *addresses = r->addresses;
   if (addresses != NULL) {
     grpc_lb_policy_args lb_policy_args;
     config = grpc_client_config_create();
@@ -183,8 +188,10 @@
     gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
     gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now);
     gpr_timespec timeout = gpr_time_sub(next_try, now);
-    gpr_log(GPR_DEBUG, "dns resolution failed: retrying in %d.%09d seconds",
-            timeout.tv_sec, timeout.tv_nsec);
+    const char *msg = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "dns resolution failed: retrying in %d.%09d seconds: %s",
+            timeout.tv_sec, timeout.tv_nsec, msg);
+    grpc_error_free_string(msg);
     GPR_ASSERT(!r->have_retry_timer);
     r->have_retry_timer = true;
     GRPC_RESOLVER_REF(&r->base, "retry-timer");
@@ -207,7 +214,9 @@
   GRPC_RESOLVER_REF(&r->base, "dns-resolving");
   GPR_ASSERT(!r->resolving);
   r->resolving = 1;
-  grpc_resolve_address(exec_ctx, r->name, r->default_port, dns_on_resolved, r);
+  r->addresses = NULL;
+  grpc_resolve_address(exec_ctx, r->name, r->default_port,
+                       grpc_closure_create(dns_on_resolved, r), &r->addresses);
 }
 
 static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx,
@@ -218,7 +227,7 @@
     if (r->resolved_config) {
       grpc_client_config_ref(r->resolved_config);
     }
-    grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
     r->next_completion = NULL;
     r->published_version = r->resolved_version;
   }
diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
index a4fa9ac..90da6a9 100644
--- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
+++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c
@@ -92,7 +92,7 @@
   gpr_mu_lock(&r->mu);
   if (r->next_completion != NULL) {
     *r->target_config = NULL;
-    grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
     r->next_completion = NULL;
   }
   gpr_mu_unlock(&r->mu);
@@ -133,7 +133,7 @@
     GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "sockaddr");
     r->published = 1;
     *r->target_config = cfg;
-    grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL);
     r->next_completion = NULL;
   }
 }
diff --git a/src/core/ext/resolver/zookeeper/zookeeper_resolver.c b/src/core/ext/resolver/zookeeper/zookeeper_resolver.c
deleted file mode 100644
index deb4b9b..0000000
--- a/src/core/ext/resolver/zookeeper/zookeeper_resolver.c
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <string.h>
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/string_util.h>
-
-#include <grpc/grpc_zookeeper.h>
-#include <zookeeper/zookeeper.h>
-
-#include "src/core/ext/client_config/lb_policy_registry.h"
-#include "src/core/ext/client_config/resolver_registry.h"
-#include "src/core/lib/iomgr/resolve_address.h"
-#include "src/core/lib/json/json.h"
-#include "src/core/lib/support/string.h"
-#include "src/core/lib/surface/api_trace.h"
-
-/** Zookeeper session expiration time in milliseconds */
-#define GRPC_ZOOKEEPER_SESSION_TIMEOUT 15000
-
-typedef struct {
-  /** base class: must be first */
-  grpc_resolver base;
-  /** refcount */
-  gpr_refcount refs;
-  /** name to resolve */
-  char *name;
-  /** subchannel factory */
-  grpc_client_channel_factory *client_channel_factory;
-  /** load balancing policy name */
-  char *lb_policy_name;
-
-  /** mutex guarding the rest of the state */
-  gpr_mu mu;
-  /** are we currently resolving? */
-  int resolving;
-  /** which version of resolved_config have we published? */
-  int published_version;
-  /** which version of resolved_config is current? */
-  int resolved_version;
-  /** pending next completion, or NULL */
-  grpc_closure *next_completion;
-  /** target config address for next completion */
-  grpc_client_config **target_config;
-  /** current (fully resolved) config */
-  grpc_client_config *resolved_config;
-
-  /** zookeeper handle */
-  zhandle_t *zookeeper_handle;
-  /** zookeeper resolved addresses */
-  grpc_resolved_addresses *resolved_addrs;
-  /** total number of addresses to be resolved */
-  int resolved_total;
-  /** number of addresses resolved */
-  int resolved_num;
-} zookeeper_resolver;
-
-static void zookeeper_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r);
-
-static void zookeeper_start_resolving_locked(zookeeper_resolver *r);
-static void zookeeper_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx,
-                                               zookeeper_resolver *r);
-
-static void zookeeper_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r);
-static void zookeeper_channel_saw_error(grpc_exec_ctx *exec_ctx,
-                                        grpc_resolver *r);
-static void zookeeper_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r,
-                           grpc_client_config **target_config,
-                           grpc_closure *on_complete);
-
-static const grpc_resolver_vtable zookeeper_resolver_vtable = {
-    zookeeper_destroy, zookeeper_shutdown, zookeeper_channel_saw_error,
-    zookeeper_next};
-
-static void zookeeper_shutdown(grpc_exec_ctx *exec_ctx,
-                               grpc_resolver *resolver) {
-  zookeeper_resolver *r = (zookeeper_resolver *)resolver;
-  grpc_closure *call = NULL;
-  gpr_mu_lock(&r->mu);
-  if (r->next_completion != NULL) {
-    *r->target_config = NULL;
-    call = r->next_completion;
-    r->next_completion = NULL;
-  }
-  zookeeper_close(r->zookeeper_handle);
-  gpr_mu_unlock(&r->mu);
-  if (call != NULL) {
-    call->cb(exec_ctx, call->cb_arg, 1);
-  }
-}
-
-static void zookeeper_channel_saw_error(grpc_exec_ctx *exec_ctx,
-                                        grpc_resolver *resolver) {
-  zookeeper_resolver *r = (zookeeper_resolver *)resolver;
-  gpr_mu_lock(&r->mu);
-  if (r->resolving == 0) {
-    zookeeper_start_resolving_locked(r);
-  }
-  gpr_mu_unlock(&r->mu);
-}
-
-static void zookeeper_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver,
-                           grpc_client_config **target_config,
-                           grpc_closure *on_complete) {
-  zookeeper_resolver *r = (zookeeper_resolver *)resolver;
-  gpr_mu_lock(&r->mu);
-  GPR_ASSERT(r->next_completion == NULL);
-  r->next_completion = on_complete;
-  r->target_config = target_config;
-  if (r->resolved_version == 0 && r->resolving == 0) {
-    zookeeper_start_resolving_locked(r);
-  } else {
-    zookeeper_maybe_finish_next_locked(exec_ctx, r);
-  }
-  gpr_mu_unlock(&r->mu);
-}
-
-/** Zookeeper global watcher for connection management
-    TODO: better connection management besides logs */
-static void zookeeper_global_watcher(zhandle_t *zookeeper_handle, int type,
-                                     int state, const char *path,
-                                     void *watcher_ctx) {
-  if (type == ZOO_SESSION_EVENT) {
-    if (state == ZOO_EXPIRED_SESSION_STATE) {
-      gpr_log(GPR_ERROR, "Zookeeper session expired");
-    } else if (state == ZOO_AUTH_FAILED_STATE) {
-      gpr_log(GPR_ERROR, "Zookeeper authentication failed");
-    }
-  }
-}
-
-/** Zookeeper watcher triggered by changes to watched nodes
-    Once triggered, it tries to resolve again to get updated addresses */
-static void zookeeper_watcher(zhandle_t *zookeeper_handle, int type, int state,
-                              const char *path, void *watcher_ctx) {
-  if (watcher_ctx != NULL) {
-    zookeeper_resolver *r = (zookeeper_resolver *)watcher_ctx;
-    if (state == ZOO_CONNECTED_STATE) {
-      gpr_mu_lock(&r->mu);
-      if (r->resolving == 0) {
-        zookeeper_start_resolving_locked(r);
-      }
-      gpr_mu_unlock(&r->mu);
-    }
-  }
-}
-
-/** Callback function after getting all resolved addresses
-    Creates a subchannel for each address */
-static void zookeeper_on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
-                                  grpc_resolved_addresses *addresses) {
-  zookeeper_resolver *r = arg;
-  grpc_client_config *config = NULL;
-  grpc_lb_policy *lb_policy;
-
-  if (addresses != NULL) {
-    grpc_lb_policy_args lb_policy_args;
-    config = grpc_client_config_create();
-    lb_policy_args.addresses = addresses;
-    lb_policy_args.client_channel_factory = r->client_channel_factory;
-    lb_policy =
-        grpc_lb_policy_create(exec_ctx, r->lb_policy_name, &lb_policy_args);
-
-    if (lb_policy != NULL) {
-      grpc_client_config_set_lb_policy(config, lb_policy);
-      GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "construction");
-    }
-    grpc_resolved_addresses_destroy(addresses);
-  }
-  gpr_mu_lock(&r->mu);
-  GPR_ASSERT(r->resolving == 1);
-  r->resolving = 0;
-  if (r->resolved_config != NULL) {
-    grpc_client_config_unref(exec_ctx, r->resolved_config);
-  }
-  r->resolved_config = config;
-  r->resolved_version++;
-  zookeeper_maybe_finish_next_locked(exec_ctx, r);
-  gpr_mu_unlock(&r->mu);
-
-  GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "zookeeper-resolving");
-}
-
-/** Callback function for each DNS resolved address */
-static void zookeeper_dns_resolved(grpc_exec_ctx *exec_ctx, void *arg,
-                                   grpc_resolved_addresses *addresses) {
-  size_t i;
-  zookeeper_resolver *r = arg;
-  int resolve_done = 0;
-
-  gpr_mu_lock(&r->mu);
-  r->resolved_num++;
-  r->resolved_addrs->addrs =
-      gpr_realloc(r->resolved_addrs->addrs,
-                  sizeof(grpc_resolved_address) *
-                      (r->resolved_addrs->naddrs + addresses->naddrs));
-  for (i = 0; i < addresses->naddrs; i++) {
-    memcpy(r->resolved_addrs->addrs[i + r->resolved_addrs->naddrs].addr,
-           addresses->addrs[i].addr, addresses->addrs[i].len);
-    r->resolved_addrs->addrs[i + r->resolved_addrs->naddrs].len =
-        addresses->addrs[i].len;
-  }
-
-  r->resolved_addrs->naddrs += addresses->naddrs;
-  grpc_resolved_addresses_destroy(addresses);
-
-  /** Wait for all addresses to be resolved */
-  resolve_done = (r->resolved_num == r->resolved_total);
-  gpr_mu_unlock(&r->mu);
-  if (resolve_done) {
-    zookeeper_on_resolved(exec_ctx, r, r->resolved_addrs);
-  }
-}
-
-/** Parses JSON format address of a zookeeper node */
-static char *zookeeper_parse_address(const char *value, size_t value_len) {
-  grpc_json *json;
-  grpc_json *cur;
-  const char *host;
-  const char *port;
-  char *buffer;
-  char *address = NULL;
-
-  buffer = gpr_malloc(value_len);
-  memcpy(buffer, value, value_len);
-  json = grpc_json_parse_string_with_len(buffer, value_len);
-  if (json != NULL) {
-    host = NULL;
-    port = NULL;
-    for (cur = json->child; cur != NULL; cur = cur->next) {
-      if (!strcmp(cur->key, "host")) {
-        host = cur->value;
-        if (port != NULL) {
-          break;
-        }
-      } else if (!strcmp(cur->key, "port")) {
-        port = cur->value;
-        if (host != NULL) {
-          break;
-        }
-      }
-    }
-    if (host != NULL && port != NULL) {
-      gpr_asprintf(&address, "%s:%s", host, port);
-    }
-    grpc_json_destroy(json);
-  }
-  gpr_free(buffer);
-
-  return address;
-}
-
-static void zookeeper_get_children_node_completion(int rc, const char *value,
-                                                   int value_len,
-                                                   const struct Stat *stat,
-                                                   const void *arg) {
-  char *address = NULL;
-  zookeeper_resolver *r = (zookeeper_resolver *)arg;
-  int resolve_done = 0;
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-
-  if (rc != 0) {
-    gpr_log(GPR_ERROR, "Error in getting a child node of %s", r->name);
-    grpc_exec_ctx_finish(&exec_ctx);
-    return;
-  }
-
-  address = zookeeper_parse_address(value, (size_t)value_len);
-  if (address != NULL) {
-    /** Further resolves address by DNS */
-    grpc_resolve_address(&exec_ctx, address, NULL, zookeeper_dns_resolved, r);
-    gpr_free(address);
-  } else {
-    gpr_log(GPR_ERROR, "Error in resolving a child node of %s", r->name);
-    gpr_mu_lock(&r->mu);
-    r->resolved_total--;
-    resolve_done = (r->resolved_num == r->resolved_total);
-    gpr_mu_unlock(&r->mu);
-    if (resolve_done) {
-      zookeeper_on_resolved(&exec_ctx, r, r->resolved_addrs);
-    }
-  }
-
-  grpc_exec_ctx_finish(&exec_ctx);
-}
-
-static void zookeeper_get_children_completion(
-    int rc, const struct String_vector *children, const void *arg) {
-  char *path;
-  int status;
-  int i;
-  zookeeper_resolver *r = (zookeeper_resolver *)arg;
-
-  if (rc != 0) {
-    gpr_log(GPR_ERROR, "Error in getting zookeeper children of %s", r->name);
-    return;
-  }
-
-  if (children->count == 0) {
-    gpr_log(GPR_ERROR, "Error in resolving zookeeper address %s", r->name);
-    return;
-  }
-
-  r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
-  r->resolved_addrs->addrs = NULL;
-  r->resolved_addrs->naddrs = 0;
-  r->resolved_total = children->count;
-
-  /** TODO: Replace expensive heap allocation with stack
-      if we can get maximum length of zookeeper path */
-  for (i = 0; i < children->count; i++) {
-    gpr_asprintf(&path, "%s/%s", r->name, children->data[i]);
-    status = zoo_awget(r->zookeeper_handle, path, zookeeper_watcher, r,
-                       zookeeper_get_children_node_completion, r);
-    gpr_free(path);
-    if (status != 0) {
-      gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", path);
-    }
-  }
-}
-
-static void zookeeper_get_node_completion(int rc, const char *value,
-                                          int value_len,
-                                          const struct Stat *stat,
-                                          const void *arg) {
-  int status;
-  char *address = NULL;
-  zookeeper_resolver *r = (zookeeper_resolver *)arg;
-  r->resolved_addrs = NULL;
-  r->resolved_total = 0;
-  r->resolved_num = 0;
-
-  if (rc != 0) {
-    gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", r->name);
-    return;
-  }
-
-  /** If zookeeper node of path r->name does not have address
-      (i.e. service node), get its children */
-  address = zookeeper_parse_address(value, (size_t)value_len);
-  if (address != NULL) {
-    r->resolved_addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
-    r->resolved_addrs->addrs = NULL;
-    r->resolved_addrs->naddrs = 0;
-    r->resolved_total = 1;
-    /** Further resolves address by DNS */
-    grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resolve_address(&exec_ctx, address, NULL, zookeeper_dns_resolved, r);
-    gpr_free(address);
-    grpc_exec_ctx_finish(&exec_ctx);
-    return;
-  }
-
-  status = zoo_awget_children(r->zookeeper_handle, r->name, zookeeper_watcher,
-                              r, zookeeper_get_children_completion, r);
-  if (status != 0) {
-    gpr_log(GPR_ERROR, "Error in getting zookeeper children of %s", r->name);
-  }
-}
-
-static void zookeeper_resolve_address(zookeeper_resolver *r) {
-  int status;
-  status = zoo_awget(r->zookeeper_handle, r->name, zookeeper_watcher, r,
-                     zookeeper_get_node_completion, r);
-  if (status != 0) {
-    gpr_log(GPR_ERROR, "Error in getting zookeeper node %s", r->name);
-  }
-}
-
-static void zookeeper_start_resolving_locked(zookeeper_resolver *r) {
-  GRPC_RESOLVER_REF(&r->base, "zookeeper-resolving");
-  GPR_ASSERT(r->resolving == 0);
-  r->resolving = 1;
-  zookeeper_resolve_address(r);
-}
-
-static void zookeeper_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx,
-                                               zookeeper_resolver *r) {
-  if (r->next_completion != NULL &&
-      r->resolved_version != r->published_version) {
-    *r->target_config = r->resolved_config;
-    if (r->resolved_config != NULL) {
-      grpc_client_config_ref(r->resolved_config);
-    }
-    grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL);
-    r->next_completion = NULL;
-    r->published_version = r->resolved_version;
-  }
-}
-
-static void zookeeper_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) {
-  zookeeper_resolver *r = (zookeeper_resolver *)gr;
-  gpr_mu_destroy(&r->mu);
-  if (r->resolved_config != NULL) {
-    grpc_client_config_unref(exec_ctx, r->resolved_config);
-  }
-  grpc_client_channel_factory_unref(exec_ctx, r->client_channel_factory);
-  gpr_free(r->name);
-  gpr_free(r->lb_policy_name);
-  gpr_free(r);
-}
-
-static grpc_resolver *zookeeper_create(grpc_resolver_args *args,
-                                       const char *lb_policy_name) {
-  zookeeper_resolver *r;
-  size_t length;
-  char *path = args->uri->path;
-
-  if (0 == strcmp(args->uri->authority, "")) {
-    gpr_log(GPR_ERROR, "No authority specified in zookeeper uri");
-    return NULL;
-  }
-
-  /** Removes the trailing slash if exists */
-  length = strlen(path);
-  if (length > 1 && path[length - 1] == '/') {
-    path[length - 1] = 0;
-  }
-
-  r = gpr_malloc(sizeof(zookeeper_resolver));
-  memset(r, 0, sizeof(*r));
-  gpr_ref_init(&r->refs, 1);
-  gpr_mu_init(&r->mu);
-  grpc_resolver_init(&r->base, &zookeeper_resolver_vtable);
-  r->name = gpr_strdup(path);
-
-  r->client_channel_factory = args->client_channel_factory;
-  grpc_client_channel_factory_ref(r->client_channel_factory);
-
-  r->lb_policy_name = gpr_strdup(lb_policy_name);
-
-  /** Initializes zookeeper client */
-  zoo_set_debug_level(ZOO_LOG_LEVEL_WARN);
-  r->zookeeper_handle =
-      zookeeper_init(args->uri->authority, zookeeper_global_watcher,
-                     GRPC_ZOOKEEPER_SESSION_TIMEOUT, 0, 0, 0);
-  if (r->zookeeper_handle == NULL) {
-    gpr_log(GPR_ERROR, "Unable to connect to zookeeper server");
-    return NULL;
-  }
-
-  return &r->base;
-}
-
-/*
- * FACTORY
- */
-
-static void zookeeper_factory_ref(grpc_resolver_factory *factory) {}
-
-static void zookeeper_factory_unref(grpc_resolver_factory *factory) {}
-
-static char *zookeeper_factory_get_default_hostname(
-    grpc_resolver_factory *factory, grpc_uri *uri) {
-  return NULL;
-}
-
-static grpc_resolver *zookeeper_factory_create_resolver(
-    grpc_resolver_factory *factory, grpc_resolver_args *args) {
-  return zookeeper_create(args, "pick_first");
-}
-
-static const grpc_resolver_factory_vtable zookeeper_factory_vtable = {
-    zookeeper_factory_ref, zookeeper_factory_unref,
-    zookeeper_factory_create_resolver, zookeeper_factory_get_default_hostname,
-    "zookeeper"};
-
-static grpc_resolver_factory zookeeper_resolver_factory = {
-    &zookeeper_factory_vtable};
-
-static grpc_resolver_factory *zookeeper_resolver_factory_create() {
-  return &zookeeper_resolver_factory;
-}
-
-static void zookeeper_plugin_init() {
-  grpc_register_resolver_type(zookeeper_resolver_factory_create());
-}
-
-void grpc_zookeeper_register() {
-  GRPC_API_TRACE("grpc_zookeeper_register(void)", 0, ());
-  grpc_register_plugin(zookeeper_plugin_init, NULL);
-}
diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.c b/src/core/ext/transport/chttp2/client/insecure/channel_create.c
index c5d3d8d..28d1be8 100644
--- a/src/core/ext/transport/chttp2/client/insecure/channel_create.c
+++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.c
@@ -79,11 +79,11 @@
 }
 
 static void on_initial_connect_string_sent(grpc_exec_ctx *exec_ctx, void *arg,
-                                           bool success) {
+                                           grpc_error *error) {
   connector_unref(exec_ctx, arg);
 }
 
-static void connected(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   connector *c = arg;
   grpc_closure *notify;
   grpc_endpoint *tcp = c->tcp;
@@ -109,7 +109,7 @@
   }
   notify = c->notify;
   c->notify = NULL;
-  notify->cb(exec_ctx, notify->cb_arg, 1);
+  notify->cb(exec_ctx, notify->cb_arg, error);
 }
 
 static void connector_shutdown(grpc_exec_ctx *exec_ctx, grpc_connector *con) {}
diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
index 58af6f9..bceef15 100644
--- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
+++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c
@@ -45,9 +45,9 @@
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/iomgr/tcp_client.h"
-#include "src/core/lib/security/auth_filters.h"
-#include "src/core/lib/security/credentials.h"
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/transport/auth_filters.h"
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/channel.h"
 #include "src/core/lib/tsi/transport_security_interface.h"
@@ -116,19 +116,19 @@
   notify = c->notify;
   c->notify = NULL;
   /* look at c->args which are connector args. */
-  notify->cb(exec_ctx, notify->cb_arg, 1);
+  notify->cb(exec_ctx, notify->cb_arg, GRPC_ERROR_NONE);
   if (args_copy != NULL) grpc_channel_args_destroy(args_copy);
 }
 
 static void on_initial_connect_string_sent(grpc_exec_ctx *exec_ctx, void *arg,
-                                           bool success) {
+                                           grpc_error *error) {
   connector *c = arg;
   grpc_channel_security_connector_do_handshake(exec_ctx, c->security_connector,
                                                c->connecting_endpoint,
                                                on_secure_handshake_done, c);
 }
 
-static void connected(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   connector *c = arg;
   grpc_closure *notify;
   grpc_endpoint *tcp = c->newly_connecting_endpoint;
@@ -153,7 +153,7 @@
     memset(c->result, 0, sizeof(*c->result));
     notify = c->notify;
     c->notify = NULL;
-    notify->cb(exec_ctx, notify->cb_arg, 1);
+    notify->cb(exec_ctx, notify->cb_arg, GRPC_ERROR_NONE);
   }
 }
 
diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
index e21fa2a..d1a58b6 100644
--- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c
@@ -35,6 +35,7 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/useful.h>
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
 #include "src/core/lib/channel/http_server_filter.h"
@@ -79,7 +80,7 @@
                     grpc_closure *destroy_done) {
   grpc_tcp_server *tcp = tcpp;
   grpc_tcp_server_unref(exec_ctx, tcp);
-  grpc_exec_ctx_enqueue(exec_ctx, destroy_done, true, NULL);
+  grpc_exec_ctx_push(exec_ctx, destroy_done, GRPC_ERROR_NONE, NULL);
 }
 
 int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) {
@@ -90,23 +91,29 @@
   int port_num = -1;
   int port_temp;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_error *err = GRPC_ERROR_NONE;
 
   GRPC_API_TRACE("grpc_server_add_insecure_http2_port(server=%p, addr=%s)", 2,
                  (server, addr));
 
-  resolved = grpc_blocking_resolve_address(addr, "http");
-  if (!resolved) {
+  grpc_error **errors = NULL;
+  err = grpc_blocking_resolve_address(addr, "https", &resolved);
+  if (err != GRPC_ERROR_NONE) {
     goto error;
   }
 
-  tcp = grpc_tcp_server_create(NULL);
-  GPR_ASSERT(tcp);
+  err = grpc_tcp_server_create(NULL, &tcp);
+  if (err != GRPC_ERROR_NONE) {
+    goto error;
+  }
 
-  for (i = 0; i < resolved->naddrs; i++) {
-    port_temp = grpc_tcp_server_add_port(
+  const size_t naddrs = resolved->naddrs;
+  errors = gpr_malloc(sizeof(*errors) * naddrs);
+  for (i = 0; i < naddrs; i++) {
+    errors[i] = grpc_tcp_server_add_port(
         tcp, (struct sockaddr *)&resolved->addrs[i].addr,
-        resolved->addrs[i].len);
-    if (port_temp > 0) {
+        resolved->addrs[i].len, &port_temp);
+    if (errors[i] == GRPC_ERROR_NONE) {
       if (port_num == -1) {
         port_num = port_temp;
       } else {
@@ -116,13 +123,22 @@
     }
   }
   if (count == 0) {
-    gpr_log(GPR_ERROR, "No address added out of total %d resolved",
-            resolved->naddrs);
+    char *msg;
+    gpr_asprintf(&msg, "No address added out of total %d resolved", naddrs);
+    err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs);
+    gpr_free(msg);
     goto error;
-  }
-  if (count != resolved->naddrs) {
-    gpr_log(GPR_ERROR, "Only %d addresses added out of total %d resolved",
-            count, resolved->naddrs);
+  } else if (count != naddrs) {
+    char *msg;
+    gpr_asprintf(&msg, "Only %d addresses added out of total %d resolved",
+                 count, naddrs);
+    err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, naddrs);
+    gpr_free(msg);
+
+    const char *warning_message = grpc_error_string(err);
+    gpr_log(GPR_INFO, "WARNING: %s", warning_message);
+    grpc_error_free_string(warning_message);
+    /* we managed to bind some addresses: continue */
   }
   grpc_resolved_addresses_destroy(resolved);
 
@@ -132,6 +148,7 @@
 
 /* Error path: cleanup and return */
 error:
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
   if (resolved) {
     grpc_resolved_addresses_destroy(resolved);
   }
@@ -142,5 +159,12 @@
 
 done:
   grpc_exec_ctx_finish(&exec_ctx);
+  if (errors != NULL) {
+    for (i = 0; i < naddrs; i++) {
+      GRPC_ERROR_UNREF(errors[i]);
+    }
+  }
+  GRPC_ERROR_UNREF(err);
+  gpr_free(errors);
   return port_num;
 }
diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
index 698b2be..629959b 100644
--- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
+++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c
@@ -37,6 +37,7 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/useful.h>
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
@@ -45,10 +46,10 @@
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/tcp_server.h"
-#include "src/core/lib/security/auth_filters.h"
-#include "src/core/lib/security/credentials.h"
-#include "src/core/lib/security/security_connector.h"
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/transport/auth_filters.h"
+#include "src/core/lib/security/transport/security_connector.h"
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/server.h"
 
@@ -139,11 +140,12 @@
                         on_accept, state);
 }
 
-static void destroy_done(grpc_exec_ctx *exec_ctx, void *statep, bool success) {
+static void destroy_done(grpc_exec_ctx *exec_ctx, void *statep,
+                         grpc_error *error) {
   grpc_server_secure_state *state = statep;
   if (state->destroy_callback != NULL) {
     state->destroy_callback->cb(exec_ctx, state->destroy_callback->cb_arg,
-                                success);
+                                GRPC_ERROR_REF(error));
   }
   grpc_server_security_connector_shutdown(exec_ctx, state->sc);
   state_unref(state);
@@ -175,6 +177,8 @@
   grpc_security_status status = GRPC_SECURITY_ERROR;
   grpc_server_security_connector *sc = NULL;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_error *err = GRPC_ERROR_NONE;
+  grpc_error **errors = NULL;
 
   GRPC_API_TRACE(
       "grpc_server_add_secure_http2_port("
@@ -185,23 +189,27 @@
   if (creds == NULL) goto error;
   status = grpc_server_credentials_create_security_connector(creds, &sc);
   if (status != GRPC_SECURITY_OK) {
-    gpr_log(GPR_ERROR,
-            "Unable to create secure server with credentials of type %s.",
-            creds->type);
+    char *msg;
+    gpr_asprintf(&msg,
+                 "Unable to create secure server with credentials of type %s.",
+                 creds->type);
+    err = grpc_error_set_int(GRPC_ERROR_CREATE(msg),
+                             GRPC_ERROR_INT_SECURITY_STATUS, status);
+    gpr_free(msg);
     goto error;
   }
   sc->channel_args = grpc_server_get_channel_args(server);
 
   /* resolve address */
-  resolved = grpc_blocking_resolve_address(addr, "https");
-  if (!resolved) {
+  err = grpc_blocking_resolve_address(addr, "https", &resolved);
+  if (err != GRPC_ERROR_NONE) {
     goto error;
   }
   state = gpr_malloc(sizeof(*state));
   memset(state, 0, sizeof(*state));
   grpc_closure_init(&state->destroy_closure, destroy_done, state);
-  tcp = grpc_tcp_server_create(&state->destroy_closure);
-  if (!tcp) {
+  err = grpc_tcp_server_create(&state->destroy_closure, &tcp);
+  if (err != GRPC_ERROR_NONE) {
     goto error;
   }
 
@@ -213,11 +221,12 @@
   gpr_mu_init(&state->mu);
   gpr_ref_init(&state->refcount, 1);
 
+  errors = gpr_malloc(sizeof(*errors) * resolved->naddrs);
   for (i = 0; i < resolved->naddrs; i++) {
-    port_temp = grpc_tcp_server_add_port(
+    errors[i] = grpc_tcp_server_add_port(
         tcp, (struct sockaddr *)&resolved->addrs[i].addr,
-        resolved->addrs[i].len);
-    if (port_temp > 0) {
+        resolved->addrs[i].len, &port_temp);
+    if (errors[i] == GRPC_ERROR_NONE) {
       if (port_num == -1) {
         port_num = port_temp;
       } else {
@@ -227,15 +236,30 @@
     }
   }
   if (count == 0) {
-    gpr_log(GPR_ERROR, "No address added out of total %d resolved",
-            resolved->naddrs);
+    char *msg;
+    gpr_asprintf(&msg, "No address added out of total %d resolved",
+                 resolved->naddrs);
+    err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, resolved->naddrs);
+    gpr_free(msg);
     goto error;
+  } else if (count != resolved->naddrs) {
+    char *msg;
+    gpr_asprintf(&msg, "Only %d addresses added out of total %d resolved",
+                 count, resolved->naddrs);
+    err = GRPC_ERROR_CREATE_REFERENCING(msg, errors, resolved->naddrs);
+    gpr_free(msg);
+
+    const char *warning_message = grpc_error_string(err);
+    gpr_log(GPR_INFO, "WARNING: %s", warning_message);
+    grpc_error_free_string(warning_message);
+    /* we managed to bind some addresses: continue */
+  } else {
+    for (i = 0; i < resolved->naddrs; i++) {
+      GRPC_ERROR_UNREF(errors[i]);
+    }
   }
-  if (count != resolved->naddrs) {
-    gpr_log(GPR_ERROR, "Only %d addresses added out of total %d resolved",
-            count, resolved->naddrs);
-    /* if it's an error, don't we want to goto error; here ? */
-  }
+  gpr_free(errors);
+  errors = NULL;
   grpc_resolved_addresses_destroy(resolved);
 
   /* Register with the server only upon success */
@@ -246,6 +270,13 @@
 
 /* Error path: cleanup and return */
 error:
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
+  if (errors != NULL) {
+    for (i = 0; i < resolved->naddrs; i++) {
+      GRPC_ERROR_UNREF(errors[i]);
+    }
+    gpr_free(errors);
+  }
   if (resolved) {
     grpc_resolved_addresses_destroy(resolved);
   }
@@ -260,5 +291,9 @@
     }
   }
   grpc_exec_ctx_finish(&exec_ctx);
+  const char *msg = grpc_error_string(err);
+  GRPC_ERROR_UNREF(err);
+  gpr_log(GPR_ERROR, "%s", msg);
+  grpc_error_free_string(msg);
   return 0;
 }
diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
index b6886a2..f48e615 100644
--- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c
+++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c
@@ -84,19 +84,17 @@
 static const grpc_transport_vtable vtable;
 
 /* forward declarations of various callbacks that we'll build closures around */
-static void writing_action(grpc_exec_ctx *exec_ctx, void *t,
-                           bool iomgr_success_ignored);
-static void reading_action(grpc_exec_ctx *exec_ctx, void *t,
-                           bool iomgr_success_ignored);
-static void parsing_action(grpc_exec_ctx *exec_ctx, void *t,
-                           bool iomgr_success_ignored);
+static void writing_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error);
+static void reading_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error);
+static void parsing_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error);
 
 /** Set a transport level setting, and push it to our peer */
 static void push_setting(grpc_chttp2_transport *t, grpc_chttp2_setting_id id,
                          uint32_t value);
 
 /** Start disconnection chain */
-static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t);
+static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
+                            grpc_error *error);
 
 /** Perform a transport_op */
 static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
@@ -133,7 +131,7 @@
 
 static void connectivity_state_set(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
-    grpc_connectivity_state state, const char *reason);
+    grpc_connectivity_state state, grpc_error *error, const char *reason);
 
 static void check_read_ops(grpc_exec_ctx *exec_ctx,
                            grpc_chttp2_transport_global *transport_global);
@@ -147,7 +145,9 @@
                                                 grpc_chttp2_stream *s,
                                                 void *byte_stream);
 static void fail_pending_writes(grpc_exec_ctx *exec_ctx,
-                                grpc_chttp2_stream_global *stream_global);
+                                grpc_chttp2_transport_global *transport_global,
+                                grpc_chttp2_stream_global *stream_global,
+                                grpc_error *error);
 
 /*******************************************************************************
  * CONSTRUCTION/DESTRUCTION/REFCOUNTING
@@ -190,7 +190,8 @@
      and maybe they hold resources that need to be freed */
   while (t->global.pings.next != &t->global.pings) {
     grpc_chttp2_outstanding_ping *ping = t->global.pings.next;
-    grpc_exec_ctx_enqueue(exec_ctx, ping->on_recv, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, ping->on_recv,
+                       GRPC_ERROR_CREATE("Transport closed"), NULL);
     ping->next->prev = ping->prev;
     ping->prev->next = ping->next;
     gpr_free(ping);
@@ -404,7 +405,7 @@
                                      grpc_chttp2_stream *s_ignored,
                                      void *arg_ignored) {
   t->destroying = 1;
-  drop_connection(exec_ctx, t);
+  drop_connection(exec_ctx, t, GRPC_ERROR_CREATE("Transport destroyed"));
 }
 
 static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) {
@@ -440,12 +441,11 @@
 
 static void close_transport_locked(grpc_exec_ctx *exec_ctx,
                                    grpc_chttp2_transport *t,
-                                   grpc_chttp2_stream *s_ignored,
-                                   void *arg_ignored) {
+                                   grpc_error *error) {
   if (!t->closed) {
     t->closed = 1;
     connectivity_state_set(exec_ctx, &t->global, GRPC_CHANNEL_FATAL_FAILURE,
-                           "close_transport");
+                           GRPC_ERROR_REF(error), "close_transport");
     if (t->ep) {
       allow_endpoint_shutdown_locked(exec_ctx, t);
     }
@@ -458,6 +458,7 @@
       GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2_writing");
     }
   }
+  GRPC_ERROR_UNREF(error);
 }
 
 #ifdef GRPC_STREAM_REFCOUNT_DEBUG
@@ -546,7 +547,9 @@
              s->global.id == 0);
   GPR_ASSERT(!s->global.in_stream_map);
   if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) {
-    close_transport_locked(exec_ctx, t, NULL, NULL);
+    close_transport_locked(
+        exec_ctx, t,
+        GRPC_ERROR_CREATE("Last stream closed after sending goaway"));
   }
   if (!t->executor.parsing_active && s->global.id) {
     GPR_ASSERT(grpc_chttp2_stream_map_find(&t->parsing_stream_map,
@@ -640,7 +643,7 @@
       t->executor.writing_active = 1;
       REF_TRANSPORT(t, "writing");
       prevent_endpoint_shutdown(t);
-      grpc_exec_ctx_enqueue(exec_ctx, &t->writing_action, true, NULL);
+      grpc_exec_ctx_push(exec_ctx, &t->writing_action, GRPC_ERROR_NONE, NULL);
     }
     check_read_ops(exec_ctx, &t->global);
 
@@ -751,12 +754,12 @@
                                         grpc_chttp2_transport *t,
                                         grpc_chttp2_stream *s_ignored,
                                         void *a) {
-  bool success = (bool)(uintptr_t)a;
+  grpc_error *error = a;
 
   allow_endpoint_shutdown_locked(exec_ctx, t);
 
-  if (!success) {
-    drop_connection(exec_ctx, t);
+  if (error != GRPC_ERROR_NONE) {
+    drop_connection(exec_ctx, t, GRPC_ERROR_REF(error));
   }
 
   grpc_chttp2_cleanup_writing(exec_ctx, &t->global, &t->writing);
@@ -764,7 +767,8 @@
   grpc_chttp2_stream_global *stream_global;
   while (grpc_chttp2_list_pop_closed_waiting_for_writing(&t->global,
                                                          &stream_global)) {
-    fail_pending_writes(exec_ctx, stream_global);
+    fail_pending_writes(exec_ctx, &t->global, stream_global,
+                        GRPC_ERROR_REF(error));
     GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "finish_writes");
   }
 
@@ -777,18 +781,18 @@
   }
 
   UNREF_TRANSPORT(exec_ctx, t, "writing");
+  GRPC_ERROR_UNREF(error);
 }
 
 void grpc_chttp2_terminate_writing(grpc_exec_ctx *exec_ctx,
-                                   void *transport_writing, bool success) {
+                                   void *transport_writing, grpc_error *error) {
   grpc_chttp2_transport *t = TRANSPORT_FROM_WRITING(transport_writing);
-  grpc_chttp2_run_with_global_lock(exec_ctx, t, NULL,
-                                   terminate_writing_with_lock,
-                                   (void *)(uintptr_t)success, 0);
+  grpc_chttp2_run_with_global_lock(
+      exec_ctx, t, NULL, terminate_writing_with_lock, GRPC_ERROR_REF(error), 0);
 }
 
 static void writing_action(grpc_exec_ctx *exec_ctx, void *gt,
-                           bool iomgr_success_ignored) {
+                           grpc_error *error) {
   grpc_chttp2_transport *t = gt;
   GPR_TIMER_BEGIN("writing_action", 0);
   grpc_chttp2_perform_writes(exec_ctx, &t->writing, t->ep);
@@ -801,11 +805,16 @@
   char *msg = gpr_dump_slice(goaway_text, GPR_DUMP_HEX | GPR_DUMP_ASCII);
   GRPC_CHTTP2_IF_TRACING(
       gpr_log(GPR_DEBUG, "got goaway [%d]: %s", goaway_error, msg));
-  gpr_free(msg);
   gpr_slice_unref(goaway_text);
   transport_global->seen_goaway = 1;
-  connectivity_state_set(exec_ctx, transport_global, GRPC_CHANNEL_FATAL_FAILURE,
-                         "got_goaway");
+  connectivity_state_set(
+      exec_ctx, transport_global, GRPC_CHANNEL_FATAL_FAILURE,
+      grpc_error_set_str(
+          grpc_error_set_int(GRPC_ERROR_CREATE("GOAWAY received"),
+                             GRPC_ERROR_INT_HTTP2_ERROR, goaway_error),
+          GRPC_ERROR_STR_RAW_BYTES, msg),
+      "got_goaway");
+  gpr_free(msg);
 }
 
 static void maybe_start_some_streams(
@@ -834,9 +843,9 @@
     transport_global->next_stream_id += 2;
 
     if (transport_global->next_stream_id >= MAX_CLIENT_STREAM_ID) {
-      connectivity_state_set(exec_ctx, transport_global,
-                             GRPC_CHANNEL_TRANSIENT_FAILURE,
-                             "no_more_stream_ids");
+      connectivity_state_set(
+          exec_ctx, transport_global, GRPC_CHANNEL_TRANSIENT_FAILURE,
+          GRPC_ERROR_CREATE("Stream IDs exhausted"), "no_more_stream_ids");
     }
 
     stream_global->outgoing_window =
@@ -864,34 +873,40 @@
 }
 
 #define CLOSURE_BARRIER_STATS_BIT (1 << 0)
-#define CLOSURE_BARRIER_FAILURE_BIT (1 << 1)
 #define CLOSURE_BARRIER_FIRST_REF_BIT (1 << 16)
 
 static grpc_closure *add_closure_barrier(grpc_closure *closure) {
-  closure->final_data += CLOSURE_BARRIER_FIRST_REF_BIT;
+  closure->next_data.scratch += CLOSURE_BARRIER_FIRST_REF_BIT;
   return closure;
 }
 
-void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx,
-                                       grpc_chttp2_stream_global *stream_global,
-                                       grpc_closure **pclosure, int success) {
+void grpc_chttp2_complete_closure_step(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
+    grpc_chttp2_stream_global *stream_global, grpc_closure **pclosure,
+    grpc_error *error) {
   grpc_closure *closure = *pclosure;
   if (closure == NULL) {
+    GRPC_ERROR_UNREF(error);
     return;
   }
-  closure->final_data -= CLOSURE_BARRIER_FIRST_REF_BIT;
-  if (!success) {
-    closure->final_data |= CLOSURE_BARRIER_FAILURE_BIT;
+  closure->next_data.scratch -= CLOSURE_BARRIER_FIRST_REF_BIT;
+  if (error != GRPC_ERROR_NONE) {
+    if (closure->error == GRPC_ERROR_NONE) {
+      closure->error =
+          GRPC_ERROR_CREATE("Error in HTTP transport completing operation");
+      closure->error = grpc_error_set_str(
+          closure->error, GRPC_ERROR_STR_TARGET_ADDRESS,
+          TRANSPORT_FROM_GLOBAL(transport_global)->peer_string);
+    }
+    closure->error = grpc_error_add_child(closure->error, error);
   }
-  if (closure->final_data < CLOSURE_BARRIER_FIRST_REF_BIT) {
-    if (closure->final_data & CLOSURE_BARRIER_STATS_BIT) {
+  if (closure->next_data.scratch < CLOSURE_BARRIER_FIRST_REF_BIT) {
+    if (closure->next_data.scratch & CLOSURE_BARRIER_STATS_BIT) {
       grpc_transport_move_stats(&stream_global->stats,
                                 stream_global->collecting_stats);
       stream_global->collecting_stats = NULL;
     }
-    grpc_exec_ctx_enqueue(
-        exec_ctx, closure,
-        (closure->final_data & CLOSURE_BARRIER_FAILURE_BIT) == 0, NULL);
+    grpc_exec_ctx_push(exec_ctx, closure, closure->error, NULL);
   }
   *pclosure = NULL;
 }
@@ -909,7 +924,7 @@
   return 0;
 }
 
-static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, bool success) {}
+static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {}
 
 static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx,
                                      grpc_chttp2_transport *t,
@@ -926,12 +941,13 @@
   }
   /* use final_data as a barrier until enqueue time; the inital counter is
      dropped at the end of this function */
-  on_complete->final_data = CLOSURE_BARRIER_FIRST_REF_BIT;
+  on_complete->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT;
+  on_complete->error = GRPC_ERROR_NONE;
 
   if (op->collect_stats != NULL) {
     GPR_ASSERT(stream_global->collecting_stats == NULL);
     stream_global->collecting_stats = op->collect_stats;
-    on_complete->final_data |= CLOSURE_BARRIER_STATS_BIT;
+    on_complete->next_data.scratch |= CLOSURE_BARRIER_STATS_BIT;
   }
 
   if (op->cancel_with_status != GRPC_STATUS_OK) {
@@ -978,8 +994,10 @@
         }
       } else {
         grpc_chttp2_complete_closure_step(
-            exec_ctx, stream_global,
-            &stream_global->send_initial_metadata_finished, 0);
+            exec_ctx, transport_global, stream_global,
+            &stream_global->send_initial_metadata_finished,
+            GRPC_ERROR_CREATE(
+                "Attempt to send initial metadata after stream was closed"));
       }
     }
   }
@@ -990,7 +1008,9 @@
     stream_global->send_message_finished = add_closure_barrier(on_complete);
     if (stream_global->write_closed) {
       grpc_chttp2_complete_closure_step(
-          exec_ctx, stream_global, &stream_global->send_message_finished, 0);
+          exec_ctx, transport_global, stream_global,
+          &stream_global->send_message_finished,
+          GRPC_ERROR_CREATE("Attempt to send message after stream was closed"));
     } else {
       stream_global->send_message = op->send_message;
       if (stream_global->id != 0) {
@@ -1024,9 +1044,12 @@
       }
       if (stream_global->write_closed) {
         grpc_chttp2_complete_closure_step(
-            exec_ctx, stream_global,
+            exec_ctx, transport_global, stream_global,
             &stream_global->send_trailing_metadata_finished,
-            grpc_metadata_batch_is_empty(op->send_trailing_metadata));
+            grpc_metadata_batch_is_empty(op->send_trailing_metadata)
+                ? GRPC_ERROR_NONE
+                : GRPC_ERROR_CREATE("Attempt to send trailing metadata after "
+                                    "stream was closed"));
       } else if (stream_global->id != 0) {
         /* TODO(ctiller): check if there's flow control for any outstanding
            bytes before going writable */
@@ -1065,7 +1088,8 @@
     grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
   }
 
-  grpc_chttp2_complete_closure_step(exec_ctx, stream_global, &on_complete, 1);
+  grpc_chttp2_complete_closure_step(exec_ctx, transport_global, stream_global,
+                                    &on_complete, GRPC_ERROR_NONE);
 
   GPR_TIMER_END("perform_stream_op_locked", 0);
 }
@@ -1102,7 +1126,7 @@
   for (ping = transport_global->pings.next; ping != &transport_global->pings;
        ping = ping->next) {
     if (0 == memcmp(opaque_8bytes, ping->id, 8)) {
-      grpc_exec_ctx_enqueue(exec_ctx, ping->on_recv, true, NULL);
+      grpc_exec_ctx_push(exec_ctx, ping->on_recv, GRPC_ERROR_NONE, NULL);
       ping->next->prev = ping->prev;
       ping->prev->next = ping->next;
       gpr_free(ping);
@@ -1124,7 +1148,7 @@
                                         grpc_chttp2_stream *s_unused,
                                         void *stream_op) {
   grpc_transport_op *op = stream_op;
-  bool close_transport = op->disconnect;
+  grpc_error *close_transport = op->disconnect_with_error;
 
   /* If there's a set_accept_stream ensure that we're not parsing
      to avoid changing things out from underneath */
@@ -1135,7 +1159,7 @@
     return;
   }
 
-  grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, true, NULL);
+  grpc_exec_ctx_push(exec_ctx, op->on_consumed, GRPC_ERROR_NONE, NULL);
 
   if (op->on_connectivity_state_change != NULL) {
     grpc_connectivity_state_notify_on_state_change(
@@ -1149,7 +1173,9 @@
         t->global.last_incoming_stream_id,
         (uint32_t)grpc_chttp2_grpc_status_to_http2_error(op->goaway_status),
         gpr_slice_ref(*op->goaway_message), &t->global.qbuf);
-    close_transport = !grpc_chttp2_has_streams(t);
+    close_transport = grpc_chttp2_has_streams(t)
+                          ? GRPC_ERROR_NONE
+                          : GRPC_ERROR_CREATE("GOAWAY sent");
   }
 
   if (op->set_accept_stream) {
@@ -1170,8 +1196,8 @@
     send_ping_locked(t, op->send_ping);
   }
 
-  if (close_transport) {
-    close_transport_locked(exec_ctx, t, NULL, NULL);
+  if (close_transport != GRPC_ERROR_NONE) {
+    close_transport_locked(exec_ctx, t, close_transport);
   }
 }
 
@@ -1207,8 +1233,8 @@
       grpc_chttp2_incoming_metadata_buffer_publish(
           &stream_global->received_initial_metadata,
           stream_global->recv_initial_metadata);
-      grpc_exec_ctx_enqueue(
-          exec_ctx, stream_global->recv_initial_metadata_ready, true, NULL);
+      grpc_exec_ctx_push(exec_ctx, stream_global->recv_initial_metadata_ready,
+                         GRPC_ERROR_NONE, NULL);
       stream_global->recv_initial_metadata_ready = NULL;
     }
     if (stream_global->recv_message_ready != NULL) {
@@ -1221,13 +1247,13 @@
         *stream_global->recv_message = grpc_chttp2_incoming_frame_queue_pop(
             &stream_global->incoming_frames);
         GPR_ASSERT(*stream_global->recv_message != NULL);
-        grpc_exec_ctx_enqueue(exec_ctx, stream_global->recv_message_ready, true,
-                              NULL);
+        grpc_exec_ctx_push(exec_ctx, stream_global->recv_message_ready,
+                           GRPC_ERROR_NONE, NULL);
         stream_global->recv_message_ready = NULL;
       } else if (stream_global->published_trailing_metadata) {
         *stream_global->recv_message = NULL;
-        grpc_exec_ctx_enqueue(exec_ctx, stream_global->recv_message_ready, true,
-                              NULL);
+        grpc_exec_ctx_push(exec_ctx, stream_global->recv_message_ready,
+                           GRPC_ERROR_NONE, NULL);
         stream_global->recv_message_ready = NULL;
       }
     }
@@ -1248,8 +1274,8 @@
             &stream_global->received_trailing_metadata,
             stream_global->recv_trailing_metadata);
         grpc_chttp2_complete_closure_step(
-            exec_ctx, stream_global,
-            &stream_global->recv_trailing_metadata_finished, 1);
+            exec_ctx, transport_global, stream_global,
+            &stream_global->recv_trailing_metadata_finished, GRPC_ERROR_NONE);
       }
     }
   }
@@ -1265,7 +1291,7 @@
 }
 
 static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
-                          uint32_t id) {
+                          uint32_t id, grpc_error *error) {
   size_t new_stream_count;
   grpc_chttp2_stream *s =
       grpc_chttp2_stream_map_delete(&t->parsing_stream_map, id);
@@ -1280,12 +1306,15 @@
   }
   if (s->parsing.data_parser.parsing_frame != NULL) {
     grpc_chttp2_incoming_byte_stream_finished(
-        exec_ctx, s->parsing.data_parser.parsing_frame, 0, 0);
+        exec_ctx, s->parsing.data_parser.parsing_frame,
+        GRPC_ERROR_CREATE_REFERENCING("Stream removed", &error, 1), 0);
     s->parsing.data_parser.parsing_frame = NULL;
   }
 
   if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) {
-    close_transport_locked(exec_ctx, t, NULL, NULL);
+    close_transport_locked(
+        exec_ctx, t,
+        GRPC_ERROR_CREATE("Last stream closed after sending GOAWAY"));
   }
   if (grpc_chttp2_list_remove_writable_stream(&t->global, &s->global)) {
     GRPC_CHTTP2_STREAM_UNREF(exec_ctx, &s->global, "chttp2_writing");
@@ -1298,6 +1327,7 @@
     t->global.concurrent_stream_count = (uint32_t)new_stream_count;
     maybe_start_some_streams(exec_ctx, &t->global);
   }
+  GRPC_ERROR_UNREF(error);
 }
 
 static void cancel_from_api(grpc_exec_ctx *exec_ctx,
@@ -1320,8 +1350,10 @@
     stream_global->seen_error = true;
     grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
   }
-  grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, 1,
-                                 1);
+  grpc_chttp2_mark_stream_closed(
+      exec_ctx, transport_global, stream_global, 1, 1,
+      grpc_error_set_int(GRPC_ERROR_CREATE("Cancelled"),
+                         GRPC_ERROR_INT_GRPC_STATUS, status));
 }
 
 void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx,
@@ -1362,23 +1394,27 @@
 }
 
 static void fail_pending_writes(grpc_exec_ctx *exec_ctx,
-                                grpc_chttp2_stream_global *stream_global) {
+                                grpc_chttp2_transport_global *transport_global,
+                                grpc_chttp2_stream_global *stream_global,
+                                grpc_error *error) {
   grpc_chttp2_complete_closure_step(
-      exec_ctx, stream_global, &stream_global->send_initial_metadata_finished,
-      0);
+      exec_ctx, transport_global, stream_global,
+      &stream_global->send_initial_metadata_finished, GRPC_ERROR_REF(error));
   grpc_chttp2_complete_closure_step(
-      exec_ctx, stream_global, &stream_global->send_trailing_metadata_finished,
-      0);
-  grpc_chttp2_complete_closure_step(exec_ctx, stream_global,
-                                    &stream_global->send_message_finished, 0);
+      exec_ctx, transport_global, stream_global,
+      &stream_global->send_trailing_metadata_finished, GRPC_ERROR_REF(error));
+  grpc_chttp2_complete_closure_step(exec_ctx, transport_global, stream_global,
+                                    &stream_global->send_message_finished,
+                                    error);
 }
 
 void grpc_chttp2_mark_stream_closed(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
-    grpc_chttp2_stream_global *stream_global, int close_reads,
-    int close_writes) {
+    grpc_chttp2_stream_global *stream_global, int close_reads, int close_writes,
+    grpc_error *error) {
   if (stream_global->read_closed && stream_global->write_closed) {
     /* already closed */
+    GRPC_ERROR_UNREF(error);
     return;
   }
   grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
@@ -1395,7 +1431,8 @@
       grpc_chttp2_list_add_closed_waiting_for_writing(transport_global,
                                                       stream_global);
     } else {
-      fail_pending_writes(exec_ctx, stream_global);
+      fail_pending_writes(exec_ctx, transport_global, stream_global,
+                          GRPC_ERROR_REF(error));
     }
   }
   if (stream_global->read_closed && stream_global->write_closed) {
@@ -1406,11 +1443,12 @@
     } else {
       if (stream_global->id != 0) {
         remove_stream(exec_ctx, TRANSPORT_FROM_GLOBAL(transport_global),
-                      stream_global->id);
+                      stream_global->id, GRPC_ERROR_REF(error));
       }
       GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2");
     }
   }
+  GRPC_ERROR_UNREF(error);
 }
 
 static void close_from_api(grpc_exec_ctx *exec_ctx,
@@ -1515,8 +1553,16 @@
   }
   grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global, status,
                           optional_message);
+  grpc_error *err = GRPC_ERROR_CREATE("Stream closed");
+  err = grpc_error_set_int(err, GRPC_ERROR_INT_GRPC_STATUS, status);
+  if (optional_message) {
+    char *str =
+        gpr_dump_slice(*optional_message, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+    err = grpc_error_set_str(err, GRPC_ERROR_STR_GRPC_MESSAGE, str);
+    gpr_free(str);
+  }
   grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, 1,
-                                 1);
+                                 1, err);
 }
 
 static void cancel_stream_cb(grpc_chttp2_transport_global *transport_global,
@@ -1531,8 +1577,9 @@
   grpc_chttp2_for_all_streams(&t->global, exec_ctx, cancel_stream_cb);
 }
 
-static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) {
-  close_transport_locked(exec_ctx, t, NULL, NULL);
+static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
+                            grpc_error *error) {
+  close_transport_locked(exec_ctx, t, error);
   end_all_the_calls(exec_ctx, t);
 }
 
@@ -1563,20 +1610,22 @@
 static void reading_action_locked(grpc_exec_ctx *exec_ctx,
                                   grpc_chttp2_transport *t,
                                   grpc_chttp2_stream *s_unused, void *arg);
-static void parsing_action(grpc_exec_ctx *exec_ctx, void *arg, bool success);
+static void parsing_action(grpc_exec_ctx *exec_ctx, void *arg,
+                           grpc_error *error);
 static void post_reading_action_locked(grpc_exec_ctx *exec_ctx,
                                        grpc_chttp2_transport *t,
                                        grpc_chttp2_stream *s_unused, void *arg);
 static void post_parse_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
                               grpc_chttp2_stream *s_unused, void *arg);
 
-static void reading_action(grpc_exec_ctx *exec_ctx, void *tp, bool success) {
+static void reading_action(grpc_exec_ctx *exec_ctx, void *tp,
+                           grpc_error *error) {
   /* Control flow:
      reading_action_locked ->
        (parse_unlocked -> post_parse_locked)? ->
        post_reading_action_locked */
   grpc_chttp2_run_with_global_lock(exec_ctx, tp, NULL, reading_action_locked,
-                                   (void *)(uintptr_t)success, 0);
+                                   GRPC_ERROR_REF(error), 0);
 }
 
 static void reading_action_locked(grpc_exec_ctx *exec_ctx,
@@ -1584,7 +1633,7 @@
                                   grpc_chttp2_stream *s_unused, void *arg) {
   grpc_chttp2_transport_global *transport_global = &t->global;
   grpc_chttp2_transport_parsing *transport_parsing = &t->parsing;
-  bool success = (bool)(uintptr_t)arg;
+  grpc_error *error = arg;
 
   GPR_ASSERT(!t->executor.parsing_active);
   if (!t->closed) {
@@ -1593,27 +1642,33 @@
     grpc_chttp2_stream_map_move_into(&t->new_stream_map,
                                      &t->parsing_stream_map);
     grpc_chttp2_prepare_to_read(transport_global, transport_parsing);
-    grpc_exec_ctx_enqueue(exec_ctx, &t->parsing_action, success, NULL);
+    grpc_exec_ctx_push(exec_ctx, &t->parsing_action, error, NULL);
   } else {
     post_reading_action_locked(exec_ctx, t, s_unused, arg);
   }
 }
 
-static void parsing_action(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void parsing_action(grpc_exec_ctx *exec_ctx, void *arg,
+                           grpc_error *error) {
   grpc_chttp2_transport *t = arg;
   GPR_TIMER_BEGIN("reading_action.parse", 0);
   size_t i = 0;
-  for (; i < t->read_buffer.count &&
-         grpc_chttp2_perform_read(exec_ctx, &t->parsing,
-                                  t->read_buffer.slices[i]);
-       i++)
-    ;
-  if (i != t->read_buffer.count) {
-    success = false;
+  grpc_error *errors[2] = {GRPC_ERROR_REF(error), GRPC_ERROR_NONE};
+  for (; i < t->read_buffer.count && errors[1] == GRPC_ERROR_NONE; i++) {
+    errors[1] = grpc_chttp2_perform_read(exec_ctx, &t->parsing,
+                                         t->read_buffer.slices[i]);
+  };
+  grpc_error *err =
+      errors[0] == GRPC_ERROR_NONE && errors[1] == GRPC_ERROR_NONE
+          ? GRPC_ERROR_NONE
+          : GRPC_ERROR_CREATE_REFERENCING("Failed parsing HTTP/2", errors,
+                                          GPR_ARRAY_SIZE(errors));
+  for (i = 0; i < GPR_ARRAY_SIZE(errors); i++) {
+    GRPC_ERROR_UNREF(errors[i]);
   }
   GPR_TIMER_END("reading_action.parse", 0);
-  grpc_chttp2_run_with_global_lock(exec_ctx, t, NULL, post_parse_locked,
-                                   (void *)(uintptr_t)success, 0);
+  grpc_chttp2_run_with_global_lock(exec_ctx, t, NULL, post_parse_locked, err,
+                                   0);
 }
 
 static void post_parse_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
@@ -1650,7 +1705,8 @@
     GPR_ASSERT(stream_global->in_stream_map);
     GPR_ASSERT(stream_global->write_closed);
     GPR_ASSERT(stream_global->read_closed);
-    remove_stream(exec_ctx, t, stream_global->id);
+    remove_stream(exec_ctx, t, stream_global->id,
+                  GRPC_ERROR_CREATE("Stream removed"));
     GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2");
   }
 
@@ -1661,10 +1717,13 @@
                                        grpc_chttp2_transport *t,
                                        grpc_chttp2_stream *s_unused,
                                        void *arg) {
-  bool success = (bool)(uintptr_t)arg;
+  grpc_error *error = arg;
   bool keep_reading = false;
-  if (!success || t->closed) {
-    drop_connection(exec_ctx, t);
+  if (error == GRPC_ERROR_NONE && t->closed) {
+    error = GRPC_ERROR_CREATE("Transport closed");
+  }
+  if (error != GRPC_ERROR_NONE) {
+    drop_connection(exec_ctx, t, GRPC_ERROR_REF(error));
     t->endpoint_reading = 0;
     if (!t->executor.writing_active && t->ep) {
       grpc_endpoint_destroy(exec_ctx, t->ep);
@@ -1686,6 +1745,8 @@
   } else {
     UNREF_TRANSPORT(exec_ctx, t, "reading_action");
   }
+
+  GRPC_ERROR_UNREF(error);
 }
 
 /*******************************************************************************
@@ -1694,13 +1755,13 @@
 
 static void connectivity_state_set(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
-    grpc_connectivity_state state, const char *reason) {
+    grpc_connectivity_state state, grpc_error *error, const char *reason) {
   GRPC_CHTTP2_IF_TRACING(
       gpr_log(GPR_DEBUG, "set connectivity_state=%d", state));
   grpc_connectivity_state_set(
       exec_ctx,
       &TRANSPORT_FROM_GLOBAL(transport_global)->channel_callback.state_tracker,
-      state, reason);
+      state, error, reason);
 }
 
 /*******************************************************************************
@@ -1739,6 +1800,7 @@
 static void incoming_byte_stream_unref(grpc_exec_ctx *exec_ctx,
                                        grpc_chttp2_incoming_byte_stream *bs) {
   if (gpr_unref(&bs->refs)) {
+    GRPC_ERROR_UNREF(bs->error);
     gpr_slice_buffer_destroy(&bs->slices);
     gpr_free(bs);
   }
@@ -1807,9 +1869,10 @@
   }
   if (bs->slices.count > 0) {
     *arg->slice = gpr_slice_buffer_take_first(&bs->slices);
-    grpc_exec_ctx_enqueue(exec_ctx, arg->on_complete, true, NULL);
-  } else if (bs->failed) {
-    grpc_exec_ctx_enqueue(exec_ctx, arg->on_complete, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, arg->on_complete, GRPC_ERROR_NONE, NULL);
+  } else if (bs->error != GRPC_ERROR_NONE) {
+    grpc_exec_ctx_push(exec_ctx, arg->on_complete, GRPC_ERROR_REF(bs->error),
+                       NULL);
   } else {
     bs->on_next = arg->on_complete;
     bs->next = arg->slice;
@@ -1866,7 +1929,7 @@
   grpc_chttp2_incoming_byte_stream *bs = arg->byte_stream;
   if (bs->on_next != NULL) {
     *bs->next = arg->slice;
-    grpc_exec_ctx_enqueue(exec_ctx, bs->on_next, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, bs->on_next, GRPC_ERROR_NONE, NULL);
     bs->on_next = NULL;
   } else {
     gpr_slice_buffer_add(&bs->slices, arg->slice);
@@ -1884,13 +1947,30 @@
                                    sizeof(arg));
 }
 
+typedef struct {
+  grpc_chttp2_incoming_byte_stream *bs;
+  grpc_error *error;
+} bs_fail_args;
+
+static bs_fail_args *make_bs_fail_args(grpc_chttp2_incoming_byte_stream *bs,
+                                       grpc_error *error) {
+  bs_fail_args *a = gpr_malloc(sizeof(*a));
+  a->bs = bs;
+  a->error = error;
+  return a;
+}
+
 static void incoming_byte_stream_finished_failed_locked(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, grpc_chttp2_stream *s,
     void *argp) {
-  grpc_chttp2_incoming_byte_stream *bs = argp;
-  grpc_exec_ctx_enqueue(exec_ctx, bs->on_next, false, NULL);
+  bs_fail_args *a = argp;
+  grpc_chttp2_incoming_byte_stream *bs = a->bs;
+  grpc_error *error = a->error;
+  gpr_free(a);
+  grpc_exec_ctx_push(exec_ctx, bs->on_next, GRPC_ERROR_REF(error), NULL);
   bs->on_next = NULL;
-  bs->failed = 1;
+  GRPC_ERROR_UNREF(bs->error);
+  bs->error = error;
   incoming_byte_stream_unref(exec_ctx, bs);
 }
 
@@ -1903,25 +1983,26 @@
 }
 
 void grpc_chttp2_incoming_byte_stream_finished(
-    grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, int success,
-    int from_parsing_thread) {
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs,
+    grpc_error *error, int from_parsing_thread) {
   if (from_parsing_thread) {
-    if (success) {
+    if (error == GRPC_ERROR_NONE) {
       grpc_chttp2_run_with_global_lock(exec_ctx, bs->transport, bs->stream,
                                        incoming_byte_stream_finished_ok_locked,
                                        bs, 0);
     } else {
-      incoming_byte_stream_finished_ok_locked(exec_ctx, bs->transport,
-                                              bs->stream, bs);
-    }
-  } else {
-    if (success) {
       grpc_chttp2_run_with_global_lock(
           exec_ctx, bs->transport, bs->stream,
-          incoming_byte_stream_finished_failed_locked, bs, 0);
+          incoming_byte_stream_finished_failed_locked,
+          make_bs_fail_args(bs, error), 0);
+    }
+  } else {
+    if (error == GRPC_ERROR_NONE) {
+      incoming_byte_stream_finished_ok_locked(exec_ctx, bs->transport,
+                                              bs->stream, bs);
     } else {
-      incoming_byte_stream_finished_failed_locked(exec_ctx, bs->transport,
-                                                  bs->stream, bs);
+      incoming_byte_stream_finished_failed_locked(
+          exec_ctx, bs->transport, bs->stream, make_bs_fail_args(bs, error));
     }
   }
 }
@@ -1944,7 +2025,7 @@
   gpr_slice_buffer_init(&incoming_byte_stream->slices);
   incoming_byte_stream->on_next = NULL;
   incoming_byte_stream->is_tail = 1;
-  incoming_byte_stream->failed = 0;
+  incoming_byte_stream->error = GRPC_ERROR_NONE;
   if (add_to_queue->head == NULL) {
     add_to_queue->head = incoming_byte_stream;
   } else {
@@ -2073,5 +2154,5 @@
   grpc_chttp2_transport *t = (grpc_chttp2_transport *)transport;
   REF_TRANSPORT(t, "reading_action"); /* matches unref inside reading_action */
   gpr_slice_buffer_addn(&t->read_buffer, slices, nslices);
-  reading_action(exec_ctx, t, 1);
+  reading_action(exec_ctx, t, GRPC_ERROR_NONE);
 }
diff --git a/src/core/ext/transport/chttp2/transport/frame.h b/src/core/ext/transport/chttp2/transport/frame.h
index 5c72d91..7776609 100644
--- a/src/core/ext/transport/chttp2/transport/frame.h
+++ b/src/core/ext/transport/chttp2/transport/frame.h
@@ -37,13 +37,7 @@
 #include <grpc/support/port_platform.h>
 #include <grpc/support/slice.h>
 
-/* Common definitions for frame handling in the chttp2 transport */
-
-typedef enum {
-  GRPC_CHTTP2_PARSE_OK,
-  GRPC_CHTTP2_STREAM_ERROR,
-  GRPC_CHTTP2_CONNECTION_ERROR
-} grpc_chttp2_parse_error;
+#include "src/core/lib/iomgr/error.h"
 
 /* defined in internal.h */
 typedef struct grpc_chttp2_stream_parsing grpc_chttp2_stream_parsing;
diff --git a/src/core/ext/transport/chttp2/transport/frame_data.c b/src/core/ext/transport/chttp2/transport/frame_data.c
index 3a6d80e..7a1ec44 100644
--- a/src/core/ext/transport/chttp2/transport/frame_data.c
+++ b/src/core/ext/transport/chttp2/transport/frame_data.c
@@ -37,24 +37,25 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/useful.h>
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/transport/transport.h"
 
-grpc_chttp2_parse_error grpc_chttp2_data_parser_init(
-    grpc_chttp2_data_parser *parser) {
+grpc_error *grpc_chttp2_data_parser_init(grpc_chttp2_data_parser *parser) {
   parser->state = GRPC_CHTTP2_DATA_FH_0;
   parser->parsing_frame = NULL;
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
 
 void grpc_chttp2_data_parser_destroy(grpc_exec_ctx *exec_ctx,
                                      grpc_chttp2_data_parser *parser) {
   grpc_byte_stream *bs;
   if (parser->parsing_frame) {
-    grpc_chttp2_incoming_byte_stream_finished(exec_ctx, parser->parsing_frame,
-                                              0, 1);
+    grpc_chttp2_incoming_byte_stream_finished(
+        exec_ctx, parser->parsing_frame, GRPC_ERROR_CREATE("Parser destroyed"),
+        1);
   }
   while (
       (bs = grpc_chttp2_incoming_frame_queue_pop(&parser->incoming_frames))) {
@@ -62,11 +63,16 @@
   }
 }
 
-grpc_chttp2_parse_error grpc_chttp2_data_parser_begin_frame(
-    grpc_chttp2_data_parser *parser, uint8_t flags) {
+grpc_error *grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser *parser,
+                                                uint8_t flags,
+                                                uint32_t stream_id) {
   if (flags & ~GRPC_CHTTP2_DATA_FLAG_END_STREAM) {
-    gpr_log(GPR_ERROR, "unsupported data flags: 0x%02x", flags);
-    return GRPC_CHTTP2_STREAM_ERROR;
+    char *msg;
+    gpr_asprintf(&msg, "unsupported data flags: 0x%02x", flags);
+    grpc_error *err = grpc_error_set_int(GRPC_ERROR_CREATE(msg),
+                                         GRPC_ERROR_INT_STREAM_ID, stream_id);
+    gpr_free(msg);
+    return err;
   }
 
   if (flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) {
@@ -75,7 +81,7 @@
     parser->is_last_frame = 0;
   }
 
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
 
 void grpc_chttp2_incoming_frame_queue_merge(
@@ -139,7 +145,7 @@
   stats->data_bytes += write_bytes;
 }
 
-grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
+grpc_error *grpc_chttp2_data_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) {
@@ -149,19 +155,20 @@
   grpc_chttp2_data_parser *p = parser;
   uint32_t message_flags;
   grpc_chttp2_incoming_byte_stream *incoming_byte_stream;
+  char *msg;
 
   if (is_last && p->is_last_frame) {
     stream_parsing->received_close = 1;
   }
 
   if (cur == end) {
-    return GRPC_CHTTP2_PARSE_OK;
+    return GRPC_ERROR_NONE;
   }
 
   switch (p->state) {
     case GRPC_CHTTP2_DATA_ERROR:
       p->state = GRPC_CHTTP2_DATA_ERROR;
-      return GRPC_CHTTP2_STREAM_ERROR;
+      return GRPC_ERROR_REF(p->error);
     fh_0:
     case GRPC_CHTTP2_DATA_FH_0:
       stream_parsing->stats.incoming.framing_bytes++;
@@ -174,13 +181,23 @@
           p->is_frame_compressed = 1; /* GPR_TRUE */
           break;
         default:
-          gpr_log(GPR_ERROR, "Bad GRPC frame type 0x%02x", p->frame_type);
+          gpr_asprintf(&msg, "Bad GRPC frame type 0x%02x", p->frame_type);
+          p->error = GRPC_ERROR_CREATE(msg);
+          p->error = grpc_error_set_int(p->error, GRPC_ERROR_INT_STREAM_ID,
+                                        stream_parsing->id);
+          gpr_free(msg);
+          msg = gpr_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+          p->error =
+              grpc_error_set_str(p->error, GRPC_ERROR_STR_RAW_BYTES, msg);
+          gpr_free(msg);
+          p->error =
+              grpc_error_set_int(p->error, GRPC_ERROR_INT_OFFSET, cur - beg);
           p->state = GRPC_CHTTP2_DATA_ERROR;
-          return GRPC_CHTTP2_STREAM_ERROR;
+          return GRPC_ERROR_REF(p->error);
       }
       if (++cur == end) {
         p->state = GRPC_CHTTP2_DATA_FH_1;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_CHTTP2_DATA_FH_1:
@@ -188,7 +205,7 @@
       p->frame_size = ((uint32_t)*cur) << 24;
       if (++cur == end) {
         p->state = GRPC_CHTTP2_DATA_FH_2;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_CHTTP2_DATA_FH_2:
@@ -196,7 +213,7 @@
       p->frame_size |= ((uint32_t)*cur) << 16;
       if (++cur == end) {
         p->state = GRPC_CHTTP2_DATA_FH_3;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_CHTTP2_DATA_FH_3:
@@ -204,7 +221,7 @@
       p->frame_size |= ((uint32_t)*cur) << 8;
       if (++cur == end) {
         p->state = GRPC_CHTTP2_DATA_FH_4;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_CHTTP2_DATA_FH_4:
@@ -225,7 +242,7 @@
       grpc_chttp2_list_add_parsing_seen_stream(transport_parsing,
                                                stream_parsing);
       if (cur == end) {
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
       uint32_t remaining = (uint32_t)(end - cur);
       if (remaining == p->frame_size) {
@@ -233,19 +250,19 @@
         grpc_chttp2_incoming_byte_stream_push(
             exec_ctx, p->parsing_frame,
             gpr_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg)));
-        grpc_chttp2_incoming_byte_stream_finished(exec_ctx, p->parsing_frame, 1,
-                                                  1);
+        grpc_chttp2_incoming_byte_stream_finished(exec_ctx, p->parsing_frame,
+                                                  GRPC_ERROR_NONE, 1);
         p->parsing_frame = NULL;
         p->state = GRPC_CHTTP2_DATA_FH_0;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       } else if (remaining > p->frame_size) {
         stream_parsing->stats.incoming.data_bytes += p->frame_size;
         grpc_chttp2_incoming_byte_stream_push(
             exec_ctx, p->parsing_frame,
             gpr_slice_sub(slice, (size_t)(cur - beg),
                           (size_t)(cur + p->frame_size - beg)));
-        grpc_chttp2_incoming_byte_stream_finished(exec_ctx, p->parsing_frame, 1,
-                                                  1);
+        grpc_chttp2_incoming_byte_stream_finished(exec_ctx, p->parsing_frame,
+                                                  GRPC_ERROR_NONE, 1);
         p->parsing_frame = NULL;
         cur += p->frame_size;
         goto fh_0; /* loop */
@@ -256,9 +273,9 @@
             gpr_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg)));
         p->frame_size -= remaining;
         stream_parsing->stats.incoming.data_bytes += remaining;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
   }
 
-  GPR_UNREACHABLE_CODE(return GRPC_CHTTP2_CONNECTION_ERROR);
+  GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
 }
diff --git a/src/core/ext/transport/chttp2/transport/frame_data.h b/src/core/ext/transport/chttp2/transport/frame_data.h
index af71f48..a21a794 100644
--- a/src/core/ext/transport/chttp2/transport/frame_data.h
+++ b/src/core/ext/transport/chttp2/transport/frame_data.h
@@ -66,6 +66,7 @@
   uint8_t is_last_frame;
   uint8_t frame_type;
   uint32_t frame_size;
+  grpc_error *error;
 
   int is_frame_compressed;
   grpc_chttp2_incoming_frame_queue incoming_frames;
@@ -79,19 +80,19 @@
     grpc_chttp2_incoming_frame_queue *q);
 
 /* initialize per-stream state for data frame parsing */
-grpc_chttp2_parse_error grpc_chttp2_data_parser_init(
-    grpc_chttp2_data_parser *parser);
+grpc_error *grpc_chttp2_data_parser_init(grpc_chttp2_data_parser *parser);
 
 void grpc_chttp2_data_parser_destroy(grpc_exec_ctx *exec_ctx,
                                      grpc_chttp2_data_parser *parser);
 
 /* start processing a new data frame */
-grpc_chttp2_parse_error grpc_chttp2_data_parser_begin_frame(
-    grpc_chttp2_data_parser *parser, uint8_t flags);
+grpc_error *grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser *parser,
+                                                uint8_t flags,
+                                                uint32_t stream_id);
 
 /* handle a slice of a data frame - is_last indicates the last slice of a
    frame */
-grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
+grpc_error *grpc_chttp2_data_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last);
diff --git a/src/core/ext/transport/chttp2/transport/frame_goaway.c b/src/core/ext/transport/chttp2/transport/frame_goaway.c
index 69accb7..c985169 100644
--- a/src/core/ext/transport/chttp2/transport/frame_goaway.c
+++ b/src/core/ext/transport/chttp2/transport/frame_goaway.c
@@ -38,6 +38,7 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 
 void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser *p) {
   p->debug_data = NULL;
@@ -47,11 +48,15 @@
   gpr_free(p->debug_data);
 }
 
-grpc_chttp2_parse_error grpc_chttp2_goaway_parser_begin_frame(
-    grpc_chttp2_goaway_parser *p, uint32_t length, uint8_t flags) {
+grpc_error *grpc_chttp2_goaway_parser_begin_frame(grpc_chttp2_goaway_parser *p,
+                                                  uint32_t length,
+                                                  uint8_t flags) {
   if (length < 8) {
-    gpr_log(GPR_ERROR, "goaway frame too short (%d bytes)", length);
-    return GRPC_CHTTP2_CONNECTION_ERROR;
+    char *msg;
+    gpr_asprintf(&msg, "goaway frame too short (%d bytes)", length);
+    grpc_error *err = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+    return err;
   }
 
   gpr_free(p->debug_data);
@@ -59,10 +64,10 @@
   p->debug_data = gpr_malloc(p->debug_length);
   p->debug_pos = 0;
   p->state = GRPC_CHTTP2_GOAWAY_LSI0;
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
 
-grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse(
+grpc_error *grpc_chttp2_goaway_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) {
@@ -75,7 +80,7 @@
     case GRPC_CHTTP2_GOAWAY_LSI0:
       if (cur == end) {
         p->state = GRPC_CHTTP2_GOAWAY_LSI0;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
       p->last_stream_id = ((uint32_t)*cur) << 24;
       ++cur;
@@ -83,7 +88,7 @@
     case GRPC_CHTTP2_GOAWAY_LSI1:
       if (cur == end) {
         p->state = GRPC_CHTTP2_GOAWAY_LSI1;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
       p->last_stream_id |= ((uint32_t)*cur) << 16;
       ++cur;
@@ -91,7 +96,7 @@
     case GRPC_CHTTP2_GOAWAY_LSI2:
       if (cur == end) {
         p->state = GRPC_CHTTP2_GOAWAY_LSI2;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
       p->last_stream_id |= ((uint32_t)*cur) << 8;
       ++cur;
@@ -99,7 +104,7 @@
     case GRPC_CHTTP2_GOAWAY_LSI3:
       if (cur == end) {
         p->state = GRPC_CHTTP2_GOAWAY_LSI3;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
       p->last_stream_id |= ((uint32_t)*cur);
       ++cur;
@@ -107,7 +112,7 @@
     case GRPC_CHTTP2_GOAWAY_ERR0:
       if (cur == end) {
         p->state = GRPC_CHTTP2_GOAWAY_ERR0;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
       p->error_code = ((uint32_t)*cur) << 24;
       ++cur;
@@ -115,7 +120,7 @@
     case GRPC_CHTTP2_GOAWAY_ERR1:
       if (cur == end) {
         p->state = GRPC_CHTTP2_GOAWAY_ERR1;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
       p->error_code |= ((uint32_t)*cur) << 16;
       ++cur;
@@ -123,7 +128,7 @@
     case GRPC_CHTTP2_GOAWAY_ERR2:
       if (cur == end) {
         p->state = GRPC_CHTTP2_GOAWAY_ERR2;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
       p->error_code |= ((uint32_t)*cur) << 8;
       ++cur;
@@ -131,7 +136,7 @@
     case GRPC_CHTTP2_GOAWAY_ERR3:
       if (cur == end) {
         p->state = GRPC_CHTTP2_GOAWAY_ERR3;
-        return GRPC_CHTTP2_PARSE_OK;
+        return GRPC_ERROR_NONE;
       }
       p->error_code |= ((uint32_t)*cur);
       ++cur;
@@ -150,9 +155,9 @@
             gpr_slice_new(p->debug_data, p->debug_length, gpr_free);
         p->debug_data = NULL;
       }
-      return GRPC_CHTTP2_PARSE_OK;
+      return GRPC_ERROR_NONE;
   }
-  GPR_UNREACHABLE_CODE(return GRPC_CHTTP2_CONNECTION_ERROR);
+  GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
 }
 
 void grpc_chttp2_goaway_append(uint32_t last_stream_id, uint32_t error_code,
diff --git a/src/core/ext/transport/chttp2/transport/frame_goaway.h b/src/core/ext/transport/chttp2/transport/frame_goaway.h
index 7c38b26..eb43034 100644
--- a/src/core/ext/transport/chttp2/transport/frame_goaway.h
+++ b/src/core/ext/transport/chttp2/transport/frame_goaway.h
@@ -63,9 +63,9 @@
 
 void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser *p);
 void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser *p);
-grpc_chttp2_parse_error grpc_chttp2_goaway_parser_begin_frame(
+grpc_error *grpc_chttp2_goaway_parser_begin_frame(
     grpc_chttp2_goaway_parser *parser, uint32_t length, uint8_t flags);
-grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse(
+grpc_error *grpc_chttp2_goaway_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last);
diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.c b/src/core/ext/transport/chttp2/transport/frame_ping.c
index 7e1815f..1f814ab 100644
--- a/src/core/ext/transport/chttp2/transport/frame_ping.c
+++ b/src/core/ext/transport/chttp2/transport/frame_ping.c
@@ -38,6 +38,7 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 
 gpr_slice grpc_chttp2_ping_create(uint8_t ack, uint8_t *opaque_8bytes) {
   gpr_slice slice = gpr_slice_malloc(9 + 8);
@@ -57,18 +58,22 @@
   return slice;
 }
 
-grpc_chttp2_parse_error grpc_chttp2_ping_parser_begin_frame(
-    grpc_chttp2_ping_parser *parser, uint32_t length, uint8_t flags) {
+grpc_error *grpc_chttp2_ping_parser_begin_frame(grpc_chttp2_ping_parser *parser,
+                                                uint32_t length,
+                                                uint8_t flags) {
   if (flags & 0xfe || length != 8) {
-    gpr_log(GPR_ERROR, "invalid ping: length=%d, flags=%02x", length, flags);
-    return GRPC_CHTTP2_CONNECTION_ERROR;
+    char *msg;
+    gpr_asprintf(&msg, "invalid ping: length=%d, flags=%02x", length, flags);
+    grpc_error *error = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+    return error;
   }
   parser->byte = 0;
   parser->is_ack = flags;
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
 
-grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse(
+grpc_error *grpc_chttp2_ping_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) {
@@ -93,5 +98,5 @@
     }
   }
 
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.h b/src/core/ext/transport/chttp2/transport/frame_ping.h
index 4f7fcc1..5a87234 100644
--- a/src/core/ext/transport/chttp2/transport/frame_ping.h
+++ b/src/core/ext/transport/chttp2/transport/frame_ping.h
@@ -46,9 +46,9 @@
 
 gpr_slice grpc_chttp2_ping_create(uint8_t ack, uint8_t *opaque_8bytes);
 
-grpc_chttp2_parse_error grpc_chttp2_ping_parser_begin_frame(
-    grpc_chttp2_ping_parser *parser, uint32_t length, uint8_t flags);
-grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse(
+grpc_error *grpc_chttp2_ping_parser_begin_frame(grpc_chttp2_ping_parser *parser,
+                                                uint32_t length, uint8_t flags);
+grpc_error *grpc_chttp2_ping_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last);
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 7f01105..08804fa 100644
--- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
+++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.c
@@ -34,7 +34,9 @@
 #include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 
 #include "src/core/ext/transport/chttp2/transport/frame.h"
 
@@ -67,18 +69,21 @@
   return slice;
 }
 
-grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame(
+grpc_error *grpc_chttp2_rst_stream_parser_begin_frame(
     grpc_chttp2_rst_stream_parser *parser, uint32_t length, uint8_t flags) {
   if (length != 4) {
-    gpr_log(GPR_ERROR, "invalid rst_stream: length=%d, flags=%02x", length,
-            flags);
-    return GRPC_CHTTP2_CONNECTION_ERROR;
+    char *msg;
+    gpr_asprintf(&msg, "invalid rst_stream: length=%d, flags=%02x", length,
+                 flags);
+    grpc_error *err = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+    return err;
   }
   parser->byte = 0;
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
 
-grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_parse(
+grpc_error *grpc_chttp2_rst_stream_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) {
@@ -97,12 +102,13 @@
   if (p->byte == 4) {
     GPR_ASSERT(is_last);
     stream_parsing->received_close = 1;
-    stream_parsing->saw_rst_stream = 1;
-    stream_parsing->rst_stream_reason = (((uint32_t)p->reason_bytes[0]) << 24) |
-                                        (((uint32_t)p->reason_bytes[1]) << 16) |
-                                        (((uint32_t)p->reason_bytes[2]) << 8) |
-                                        (((uint32_t)p->reason_bytes[3]));
+    stream_parsing->forced_close_error = grpc_error_set_int(
+        GRPC_ERROR_CREATE("RST_STREAM"), GRPC_ERROR_INT_HTTP2_ERROR,
+        (((uint32_t)p->reason_bytes[0]) << 24) |
+            (((uint32_t)p->reason_bytes[1]) << 16) |
+            (((uint32_t)p->reason_bytes[2]) << 8) |
+            (((uint32_t)p->reason_bytes[3])));
   }
 
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.h b/src/core/ext/transport/chttp2/transport/frame_rst_stream.h
index 9c1e756..11cf94f 100644
--- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.h
+++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.h
@@ -47,9 +47,9 @@
 gpr_slice grpc_chttp2_rst_stream_create(uint32_t stream_id, uint32_t code,
                                         grpc_transport_one_way_stats *stats);
 
-grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame(
+grpc_error *grpc_chttp2_rst_stream_parser_begin_frame(
     grpc_chttp2_rst_stream_parser *parser, uint32_t length, uint8_t flags);
-grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_parse(
+grpc_error *grpc_chttp2_rst_stream_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last);
diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.c b/src/core/ext/transport/chttp2/transport/frame_settings.c
index a3c1e15..04b96c4 100644
--- a/src/core/ext/transport/chttp2/transport/frame_settings.c
+++ b/src/core/ext/transport/chttp2/transport/frame_settings.c
@@ -36,7 +36,9 @@
 
 #include <string.h>
 
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/useful.h>
 
 #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
@@ -118,7 +120,7 @@
   return output;
 }
 
-grpc_chttp2_parse_error grpc_chttp2_settings_parser_begin_frame(
+grpc_error *grpc_chttp2_settings_parser_begin_frame(
     grpc_chttp2_settings_parser *parser, uint32_t length, uint8_t flags,
     uint32_t *settings) {
   parser->target_settings = settings;
@@ -129,31 +131,29 @@
   if (flags == GRPC_CHTTP2_FLAG_ACK) {
     parser->is_ack = 1;
     if (length != 0) {
-      gpr_log(GPR_ERROR, "non-empty settings ack frame received");
-      return GRPC_CHTTP2_CONNECTION_ERROR;
+      return GRPC_ERROR_CREATE("non-empty settings ack frame received");
     }
-    return GRPC_CHTTP2_PARSE_OK;
+    return GRPC_ERROR_NONE;
   } else if (flags != 0) {
-    gpr_log(GPR_ERROR, "invalid flags on settings frame");
-    return GRPC_CHTTP2_CONNECTION_ERROR;
+    return GRPC_ERROR_CREATE("invalid flags on settings frame");
   } else if (length % 6 != 0) {
-    gpr_log(GPR_ERROR, "settings frames must be a multiple of six bytes");
-    return GRPC_CHTTP2_CONNECTION_ERROR;
+    return GRPC_ERROR_CREATE("settings frames must be a multiple of six bytes");
   } else {
-    return GRPC_CHTTP2_PARSE_OK;
+    return GRPC_ERROR_NONE;
   }
 }
 
-grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse(
+grpc_error *grpc_chttp2_settings_parser_parse(
     grpc_exec_ctx *exec_ctx, void *p,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) {
   grpc_chttp2_settings_parser *parser = p;
   const uint8_t *cur = GPR_SLICE_START_PTR(slice);
   const uint8_t *end = GPR_SLICE_END_PTR(slice);
+  char *msg;
 
   if (parser->is_ack) {
-    return GRPC_CHTTP2_PARSE_OK;
+    return GRPC_ERROR_NONE;
   }
 
   for (;;) {
@@ -168,7 +168,7 @@
             gpr_slice_buffer_add(&transport_parsing->qbuf,
                                  grpc_chttp2_settings_ack_create());
           }
-          return GRPC_CHTTP2_PARSE_OK;
+          return GRPC_ERROR_NONE;
         }
         parser->id = (uint16_t)(((uint16_t)*cur) << 8);
         cur++;
@@ -176,7 +176,7 @@
       case GRPC_CHTTP2_SPS_ID1:
         if (cur == end) {
           parser->state = GRPC_CHTTP2_SPS_ID1;
-          return GRPC_CHTTP2_PARSE_OK;
+          return GRPC_ERROR_NONE;
         }
         parser->id = (uint16_t)(parser->id | (*cur));
         cur++;
@@ -184,7 +184,7 @@
       case GRPC_CHTTP2_SPS_VAL0:
         if (cur == end) {
           parser->state = GRPC_CHTTP2_SPS_VAL0;
-          return GRPC_CHTTP2_PARSE_OK;
+          return GRPC_ERROR_NONE;
         }
         parser->value = ((uint32_t)*cur) << 24;
         cur++;
@@ -192,7 +192,7 @@
       case GRPC_CHTTP2_SPS_VAL1:
         if (cur == end) {
           parser->state = GRPC_CHTTP2_SPS_VAL1;
-          return GRPC_CHTTP2_PARSE_OK;
+          return GRPC_ERROR_NONE;
         }
         parser->value |= ((uint32_t)*cur) << 16;
         cur++;
@@ -200,7 +200,7 @@
       case GRPC_CHTTP2_SPS_VAL2:
         if (cur == end) {
           parser->state = GRPC_CHTTP2_SPS_VAL2;
-          return GRPC_CHTTP2_PARSE_OK;
+          return GRPC_ERROR_NONE;
         }
         parser->value |= ((uint32_t)*cur) << 8;
         cur++;
@@ -208,7 +208,7 @@
       case GRPC_CHTTP2_SPS_VAL3:
         if (cur == end) {
           parser->state = GRPC_CHTTP2_SPS_VAL3;
-          return GRPC_CHTTP2_PARSE_OK;
+          return GRPC_ERROR_NONE;
         } else {
           parser->state = GRPC_CHTTP2_SPS_ID0;
         }
@@ -229,9 +229,11 @@
                     transport_parsing->last_incoming_stream_id, sp->error_value,
                     gpr_slice_from_static_string("HTTP2 settings error"),
                     &transport_parsing->qbuf);
-                gpr_log(GPR_ERROR, "invalid value %u passed for %s",
-                        parser->value, sp->name);
-                return GRPC_CHTTP2_CONNECTION_ERROR;
+                gpr_asprintf(&msg, "invalid value %u passed for %s",
+                             parser->value, sp->name);
+                grpc_error *err = GRPC_ERROR_CREATE(msg);
+                gpr_free(msg);
+                return err;
             }
           }
           if (parser->id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE &&
@@ -249,7 +251,7 @@
                     transport_parsing->is_client ? "CLI" : "SVR", parser->id,
                     parser->value);
           }
-        } else {
+        } else if (grpc_http_trace) {
           gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)",
                   parser->id, parser->value);
         }
diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.h b/src/core/ext/transport/chttp2/transport/frame_settings.h
index d9e30f1..f654c59 100644
--- a/src/core/ext/transport/chttp2/transport/frame_settings.h
+++ b/src/core/ext/transport/chttp2/transport/frame_settings.h
@@ -92,10 +92,10 @@
 /* Create an ack settings frame */
 gpr_slice grpc_chttp2_settings_ack_create(void);
 
-grpc_chttp2_parse_error grpc_chttp2_settings_parser_begin_frame(
+grpc_error *grpc_chttp2_settings_parser_begin_frame(
     grpc_chttp2_settings_parser *parser, uint32_t length, uint8_t flags,
     uint32_t *settings);
-grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse(
+grpc_error *grpc_chttp2_settings_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last);
diff --git a/src/core/ext/transport/chttp2/transport/frame_window_update.c b/src/core/ext/transport/chttp2/transport/frame_window_update.c
index 9024341..3cf848f 100644
--- a/src/core/ext/transport/chttp2/transport/frame_window_update.c
+++ b/src/core/ext/transport/chttp2/transport/frame_window_update.c
@@ -34,7 +34,9 @@
 #include "src/core/ext/transport/chttp2/transport/frame_window_update.h"
 #include "src/core/ext/transport/chttp2/transport/internal.h"
 
+#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 
 gpr_slice grpc_chttp2_window_update_create(
     uint32_t id, uint32_t window_update, grpc_transport_one_way_stats *stats) {
@@ -62,19 +64,22 @@
   return slice;
 }
 
-grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame(
+grpc_error *grpc_chttp2_window_update_parser_begin_frame(
     grpc_chttp2_window_update_parser *parser, uint32_t length, uint8_t flags) {
   if (flags || length != 4) {
-    gpr_log(GPR_ERROR, "invalid window update: length=%d, flags=%02x", length,
-            flags);
-    return GRPC_CHTTP2_CONNECTION_ERROR;
+    char *msg;
+    gpr_asprintf(&msg, "invalid window update: length=%d, flags=%02x", length,
+                 flags);
+    grpc_error *err = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+    return err;
   }
   parser->byte = 0;
   parser->amount = 0;
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
 
-grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse(
+grpc_error *grpc_chttp2_window_update_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) {
@@ -96,8 +101,11 @@
   if (p->byte == 4) {
     uint32_t received_update = p->amount;
     if (received_update == 0 || (received_update & 0x80000000u)) {
-      gpr_log(GPR_ERROR, "invalid window update bytes: %d", p->amount);
-      return GRPC_CHTTP2_CONNECTION_ERROR;
+      char *msg;
+      gpr_asprintf(&msg, "invalid window update bytes: %d", p->amount);
+      grpc_error *err = GRPC_ERROR_CREATE(msg);
+      gpr_free(msg);
+      return err;
     }
     GPR_ASSERT(is_last);
 
@@ -115,5 +123,5 @@
     }
   }
 
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
diff --git a/src/core/ext/transport/chttp2/transport/frame_window_update.h b/src/core/ext/transport/chttp2/transport/frame_window_update.h
index d6e87b9..1bcbbf9 100644
--- a/src/core/ext/transport/chttp2/transport/frame_window_update.h
+++ b/src/core/ext/transport/chttp2/transport/frame_window_update.h
@@ -48,9 +48,9 @@
 gpr_slice grpc_chttp2_window_update_create(uint32_t id, uint32_t window_delta,
                                            grpc_transport_one_way_stats *stats);
 
-grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame(
+grpc_error *grpc_chttp2_window_update_parser_begin_frame(
     grpc_chttp2_window_update_parser *parser, uint32_t length, uint8_t flags);
-grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse(
+grpc_error *grpc_chttp2_window_update_parser_parse(
     grpc_exec_ctx *exec_ctx, void *parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last);
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.c b/src/core/ext/transport/chttp2/transport/hpack_parser.c
index 687936b..003f31f 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.c
@@ -46,6 +46,7 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/port_platform.h>
+#include <grpc/support/string_util.h>
 #include <grpc/support/useful.h>
 
 #include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
@@ -77,63 +78,70 @@
    a set of indirect jumps, and so not waste stack space. */
 
 /* forward declarations for parsing states */
-static int parse_begin(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                       const uint8_t *end);
-static int parse_error(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                       const uint8_t *end);
-static int parse_illegal_op(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                            const uint8_t *end);
+static grpc_error *parse_begin(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                               const uint8_t *end);
+static grpc_error *parse_error(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                               const uint8_t *end, grpc_error *error);
+static grpc_error *still_parse_error(grpc_chttp2_hpack_parser *p,
+                                     const uint8_t *cur, const uint8_t *end);
+static grpc_error *parse_illegal_op(grpc_chttp2_hpack_parser *p,
+                                    const uint8_t *cur, const uint8_t *end);
 
-static int parse_string_prefix(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end);
-static int parse_key_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                            const uint8_t *end);
-static int parse_value_string_with_indexed_key(grpc_chttp2_hpack_parser *p,
-                                               const uint8_t *cur,
-                                               const uint8_t *end);
-static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p,
-                                               const uint8_t *cur,
-                                               const uint8_t *end);
+static grpc_error *parse_string_prefix(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end);
+static grpc_error *parse_key_string(grpc_chttp2_hpack_parser *p,
+                                    const uint8_t *cur, const uint8_t *end);
+static grpc_error *parse_value_string_with_indexed_key(
+    grpc_chttp2_hpack_parser *p, const uint8_t *cur, const uint8_t *end);
+static grpc_error *parse_value_string_with_literal_key(
+    grpc_chttp2_hpack_parser *p, const uint8_t *cur, const uint8_t *end);
 
-static int parse_value0(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end);
-static int parse_value1(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end);
-static int parse_value2(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end);
-static int parse_value3(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end);
-static int parse_value4(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end);
-static int parse_value5up(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                          const uint8_t *end);
-
-static int parse_indexed_field(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end);
-static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end);
-static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end);
-static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end);
-static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end);
-static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end);
-static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end);
-static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end);
-static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end);
-static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end);
-static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end);
-static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                              const uint8_t *end);
-static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+static grpc_error *parse_value0(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
                                 const uint8_t *end);
+static grpc_error *parse_value1(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                                const uint8_t *end);
+static grpc_error *parse_value2(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                                const uint8_t *end);
+static grpc_error *parse_value3(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                                const uint8_t *end);
+static grpc_error *parse_value4(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                                const uint8_t *end);
+static grpc_error *parse_value5up(grpc_chttp2_hpack_parser *p,
+                                  const uint8_t *cur, const uint8_t *end);
+
+static grpc_error *parse_indexed_field(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end);
+static grpc_error *parse_indexed_field_x(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end);
+static grpc_error *parse_lithdr_incidx(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end);
+static grpc_error *parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end);
+static grpc_error *parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end);
+static grpc_error *parse_lithdr_notidx(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end);
+static grpc_error *parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end);
+static grpc_error *parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end);
+static grpc_error *parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end);
+static grpc_error *parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end);
+static grpc_error *parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end);
+static grpc_error *parse_max_tbl_size(grpc_chttp2_hpack_parser *p,
+                                      const uint8_t *cur, const uint8_t *end);
+static grpc_error *parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p,
+                                        const uint8_t *cur, const uint8_t *end);
 
 /* we translate the first byte of a hpack field into one of these decoding
    cases, then use a lookup table to jump directly to the appropriate parser.
@@ -631,19 +639,18 @@
 };
 
 /* emission helpers */
-static int on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
-                  int add_to_table) {
+static grpc_error *on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
+                          int add_to_table) {
   if (add_to_table) {
-    if (!grpc_chttp2_hptbl_add(&p->table, md)) {
-      return 0;
-    }
+    grpc_error *err = grpc_chttp2_hptbl_add(&p->table, md);
+    if (err != GRPC_ERROR_NONE) return err;
   }
   if (p->on_header == NULL) {
     GRPC_MDELEM_UNREF(md);
-    return 0;
+    return GRPC_ERROR_CREATE("on_header callback not set");
   }
   p->on_header(p->on_header_user_data, md);
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
 static grpc_mdstr *take_string(grpc_chttp2_hpack_parser *p,
@@ -654,70 +661,70 @@
 }
 
 /* jump to the next state */
-static int parse_next(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                      const uint8_t *end) {
+static grpc_error *parse_next(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                              const uint8_t *end) {
   p->state = *p->next_state++;
   return p->state(p, cur, end);
 }
 
 /* begin parsing a header: all functionality is encoded into lookup tables
    above */
-static int parse_begin(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                       const uint8_t *end) {
+static grpc_error *parse_begin(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                               const uint8_t *end) {
   if (cur == end) {
     p->state = parse_begin;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   return first_byte_action[first_byte_lut[*cur]](p, cur, end);
 }
 
 /* stream dependency and prioritization data: we just skip it */
-static int parse_stream_weight(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end) {
+static grpc_error *parse_stream_weight(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end) {
   if (cur == end) {
     p->state = parse_stream_weight;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   return p->after_prioritization(p, cur + 1, end);
 }
 
-static int parse_stream_dep3(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                             const uint8_t *end) {
+static grpc_error *parse_stream_dep3(grpc_chttp2_hpack_parser *p,
+                                     const uint8_t *cur, const uint8_t *end) {
   if (cur == end) {
     p->state = parse_stream_dep3;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   return parse_stream_weight(p, cur + 1, end);
 }
 
-static int parse_stream_dep2(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                             const uint8_t *end) {
+static grpc_error *parse_stream_dep2(grpc_chttp2_hpack_parser *p,
+                                     const uint8_t *cur, const uint8_t *end) {
   if (cur == end) {
     p->state = parse_stream_dep2;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   return parse_stream_dep3(p, cur + 1, end);
 }
 
-static int parse_stream_dep1(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                             const uint8_t *end) {
+static grpc_error *parse_stream_dep1(grpc_chttp2_hpack_parser *p,
+                                     const uint8_t *cur, const uint8_t *end) {
   if (cur == end) {
     p->state = parse_stream_dep1;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   return parse_stream_dep2(p, cur + 1, end);
 }
 
-static int parse_stream_dep0(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                             const uint8_t *end) {
+static grpc_error *parse_stream_dep0(grpc_chttp2_hpack_parser *p,
+                                     const uint8_t *cur, const uint8_t *end) {
   if (cur == end) {
     p->state = parse_stream_dep0;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   return parse_stream_dep1(p, cur + 1, end);
@@ -725,30 +732,34 @@
 
 /* emit an indexed field; for now just logs it to console; jumps to
    begin the next field on completion */
-static int finish_indexed_field(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                                const uint8_t *end) {
+static grpc_error *finish_indexed_field(grpc_chttp2_hpack_parser *p,
+                                        const uint8_t *cur,
+                                        const uint8_t *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   if (md == NULL) {
-    if (grpc_http_trace) {
-      gpr_log(GPR_ERROR, "Invalid HPACK index received: %d", p->index);
-    }
-    return 0;
+    return grpc_error_set_int(
+        grpc_error_set_int(GRPC_ERROR_CREATE("Invalid HPACK index received"),
+                           GRPC_ERROR_INT_INDEX, p->index),
+        GRPC_ERROR_INT_SIZE, p->table.num_ents);
   }
   GRPC_MDELEM_REF(md);
-  return on_hdr(p, md, 0) && parse_begin(p, cur, end);
+  grpc_error *err = on_hdr(p, md, 0);
+  if (err != GRPC_ERROR_NONE) return err;
+  return parse_begin(p, cur, end);
 }
 
 /* parse an indexed field with index < 127 */
-static int parse_indexed_field(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end) {
+static grpc_error *parse_indexed_field(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end) {
   p->dynamic_table_update_allowed = 0;
   p->index = (*cur) & 0x7f;
   return finish_indexed_field(p, cur + 1, end);
 }
 
 /* parse an indexed field with index >= 127 */
-static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end) {
+static grpc_error *parse_indexed_field_x(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       finish_indexed_field};
   p->dynamic_table_update_allowed = 0;
@@ -760,28 +771,34 @@
 
 /* finish a literal header with incremental indexing: just log, and jump to '
    begin */
-static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                                const uint8_t *end) {
+static grpc_error *finish_lithdr_incidx(grpc_chttp2_hpack_parser *p,
+                                        const uint8_t *cur,
+                                        const uint8_t *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   GPR_ASSERT(md != NULL); /* handled in string parsing */
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
-                                                     take_string(p, &p->value)),
-                1) &&
-         parse_begin(p, cur, end);
+  grpc_error *err =
+      on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
+                                                  take_string(p, &p->value)),
+             1);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
 }
 
 /* finish a literal header with incremental indexing with no index */
-static int finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
-                                  const uint8_t *cur, const uint8_t *end) {
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
-                                                     take_string(p, &p->value)),
-                1) &&
-         parse_begin(p, cur, end);
+static grpc_error *finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
+                                          const uint8_t *cur,
+                                          const uint8_t *end) {
+  grpc_error *err =
+      on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
+                                                  take_string(p, &p->value)),
+             1);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
 }
 
 /* parse a literal header with incremental indexing; index < 63 */
-static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end) {
+static grpc_error *parse_lithdr_incidx(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       parse_value_string_with_indexed_key, finish_lithdr_incidx};
   p->dynamic_table_update_allowed = 0;
@@ -791,8 +808,9 @@
 }
 
 /* parse a literal header with incremental indexing; index >= 63 */
-static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end) {
+static grpc_error *parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       parse_string_prefix, parse_value_string_with_indexed_key,
       finish_lithdr_incidx};
@@ -804,8 +822,9 @@
 }
 
 /* parse a literal header with incremental indexing; index = 0 */
-static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end) {
+static grpc_error *parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       parse_key_string, parse_string_prefix,
       parse_value_string_with_literal_key, finish_lithdr_incidx_v};
@@ -815,28 +834,34 @@
 }
 
 /* finish a literal header without incremental indexing */
-static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                                const uint8_t *end) {
+static grpc_error *finish_lithdr_notidx(grpc_chttp2_hpack_parser *p,
+                                        const uint8_t *cur,
+                                        const uint8_t *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   GPR_ASSERT(md != NULL); /* handled in string parsing */
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
-                                                     take_string(p, &p->value)),
-                0) &&
-         parse_begin(p, cur, end);
+  grpc_error *err =
+      on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
+                                                  take_string(p, &p->value)),
+             0);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
 }
 
 /* finish a literal header without incremental indexing with index = 0 */
-static int finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
-                                  const uint8_t *cur, const uint8_t *end) {
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
-                                                     take_string(p, &p->value)),
-                0) &&
-         parse_begin(p, cur, end);
+static grpc_error *finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
+                                          const uint8_t *cur,
+                                          const uint8_t *end) {
+  grpc_error *err =
+      on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
+                                                  take_string(p, &p->value)),
+             0);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
 }
 
 /* parse a literal header without incremental indexing; index < 15 */
-static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end) {
+static grpc_error *parse_lithdr_notidx(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       parse_value_string_with_indexed_key, finish_lithdr_notidx};
   p->dynamic_table_update_allowed = 0;
@@ -846,8 +871,9 @@
 }
 
 /* parse a literal header without incremental indexing; index >= 15 */
-static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end) {
+static grpc_error *parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       parse_string_prefix, parse_value_string_with_indexed_key,
       finish_lithdr_notidx};
@@ -859,8 +885,9 @@
 }
 
 /* parse a literal header without incremental indexing; index == 0 */
-static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end) {
+static grpc_error *parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       parse_key_string, parse_string_prefix,
       parse_value_string_with_literal_key, finish_lithdr_notidx_v};
@@ -870,28 +897,34 @@
 }
 
 /* finish a literal header that is never indexed */
-static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                                const uint8_t *end) {
+static grpc_error *finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
+                                        const uint8_t *cur,
+                                        const uint8_t *end) {
   grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   GPR_ASSERT(md != NULL); /* handled in string parsing */
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
-                                                     take_string(p, &p->value)),
-                0) &&
-         parse_begin(p, cur, end);
+  grpc_error *err =
+      on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
+                                                  take_string(p, &p->value)),
+             0);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
 }
 
 /* finish a literal header that is never indexed with an extra value */
-static int finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
-                                  const uint8_t *cur, const uint8_t *end) {
-  return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
-                                                     take_string(p, &p->value)),
-                0) &&
-         parse_begin(p, cur, end);
+static grpc_error *finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
+                                          const uint8_t *cur,
+                                          const uint8_t *end) {
+  grpc_error *err =
+      on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
+                                                  take_string(p, &p->value)),
+             0);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
 }
 
 /* parse a literal header that is never indexed; index < 15 */
-static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end) {
+static grpc_error *parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       parse_value_string_with_indexed_key, finish_lithdr_nvridx};
   p->dynamic_table_update_allowed = 0;
@@ -901,8 +934,9 @@
 }
 
 /* parse a literal header that is never indexed; index >= 15 */
-static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end) {
+static grpc_error *parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       parse_string_prefix, parse_value_string_with_indexed_key,
       finish_lithdr_nvridx};
@@ -914,8 +948,9 @@
 }
 
 /* parse a literal header that is never indexed; index == 0 */
-static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
-                                 const uint8_t *cur, const uint8_t *end) {
+static grpc_error *parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
+                                         const uint8_t *cur,
+                                         const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       parse_key_string, parse_string_prefix,
       parse_value_string_with_literal_key, finish_lithdr_nvridx_v};
@@ -925,20 +960,25 @@
 }
 
 /* finish parsing a max table size change */
-static int finish_max_tbl_size(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end) {
+static grpc_error *finish_max_tbl_size(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end) {
   if (grpc_http_trace) {
     gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index);
   }
-  return grpc_chttp2_hptbl_set_current_table_size(&p->table, p->index) &&
-         parse_begin(p, cur, end);
+  grpc_error *err =
+      grpc_chttp2_hptbl_set_current_table_size(&p->table, p->index);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_begin(p, cur, end);
 }
 
 /* parse a max table size change, max size < 15 */
-static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                              const uint8_t *end) {
+static grpc_error *parse_max_tbl_size(grpc_chttp2_hpack_parser *p,
+                                      const uint8_t *cur, const uint8_t *end) {
   if (p->dynamic_table_update_allowed == 0) {
-    return 0;
+    return parse_error(
+        p, cur, end,
+        GRPC_ERROR_CREATE(
+            "More than two max table size changes in a single frame"));
   }
   p->dynamic_table_update_allowed--;
   p->index = (*cur) & 0x1f;
@@ -946,12 +986,16 @@
 }
 
 /* parse a max table size change, max size >= 15 */
-static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                                const uint8_t *end) {
+static grpc_error *parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p,
+                                        const uint8_t *cur,
+                                        const uint8_t *end) {
   static const grpc_chttp2_hpack_parser_state and_then[] = {
       finish_max_tbl_size};
   if (p->dynamic_table_update_allowed == 0) {
-    return 0;
+    return parse_error(
+        p, cur, end,
+        GRPC_ERROR_CREATE(
+            "More than two max table size changes in a single frame"));
   }
   p->dynamic_table_update_allowed--;
   p->next_state = and_then;
@@ -961,28 +1005,38 @@
 }
 
 /* a parse error: jam the parse state into parse_error, and return error */
-static int parse_error(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                       const uint8_t *end) {
-  p->state = parse_error;
-  return 0;
+static grpc_error *parse_error(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                               const uint8_t *end, grpc_error *err) {
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
+  if (p->last_error == GRPC_ERROR_NONE) {
+    p->last_error = GRPC_ERROR_REF(err);
+  }
+  p->state = still_parse_error;
+  return err;
 }
 
-static int parse_illegal_op(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                            const uint8_t *end) {
+static grpc_error *still_parse_error(grpc_chttp2_hpack_parser *p,
+                                     const uint8_t *cur, const uint8_t *end) {
+  return GRPC_ERROR_REF(p->last_error);
+}
+
+static grpc_error *parse_illegal_op(grpc_chttp2_hpack_parser *p,
+                                    const uint8_t *cur, const uint8_t *end) {
   GPR_ASSERT(cur != end);
-  if (grpc_http_trace) {
-    gpr_log(GPR_DEBUG, "Illegal hpack op code %d", *cur);
-  }
-  return parse_error(p, cur, end);
+  char *msg;
+  gpr_asprintf(&msg, "Illegal hpack op code %d", *cur);
+  grpc_error *err = GRPC_ERROR_CREATE(msg);
+  gpr_free(msg);
+  return parse_error(p, cur, end, err);
 }
 
 /* parse the 1st byte of a varint into p->parsing.value
    no overflow is possible */
-static int parse_value0(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end) {
+static grpc_error *parse_value0(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                                const uint8_t *end) {
   if (cur == end) {
     p->state = parse_value0;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   *p->parsing.value += (*cur) & 0x7f;
@@ -996,11 +1050,11 @@
 
 /* parse the 2nd byte of a varint into p->parsing.value
    no overflow is possible */
-static int parse_value1(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end) {
+static grpc_error *parse_value1(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                                const uint8_t *end) {
   if (cur == end) {
     p->state = parse_value1;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 7;
@@ -1014,11 +1068,11 @@
 
 /* parse the 3rd byte of a varint into p->parsing.value
    no overflow is possible */
-static int parse_value2(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end) {
+static grpc_error *parse_value2(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                                const uint8_t *end) {
   if (cur == end) {
     p->state = parse_value2;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 14;
@@ -1032,11 +1086,11 @@
 
 /* parse the 4th byte of a varint into p->parsing.value
    no overflow is possible */
-static int parse_value3(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end) {
+static grpc_error *parse_value3(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                                const uint8_t *end) {
   if (cur == end) {
     p->state = parse_value3;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 21;
@@ -1050,15 +1104,16 @@
 
 /* parse the 5th byte of a varint into p->parsing.value
    depending on the byte, we may overflow, and care must be taken */
-static int parse_value4(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end) {
+static grpc_error *parse_value4(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                                const uint8_t *end) {
   uint8_t c;
   uint32_t cur_value;
   uint32_t add_value;
+  char *msg;
 
   if (cur == end) {
     p->state = parse_value4;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   c = (*cur) & 0x7f;
@@ -1081,48 +1136,49 @@
   }
 
 error:
-  if (grpc_http_trace) {
-    gpr_log(GPR_ERROR,
-            "integer overflow in hpack integer decoding: have 0x%08x, "
-            "got byte 0x%02x on byte 5",
-            *p->parsing.value, *cur);
-  }
-  return parse_error(p, cur, end);
+  gpr_asprintf(&msg,
+               "integer overflow in hpack integer decoding: have 0x%08x, "
+               "got byte 0x%02x on byte 5",
+               *p->parsing.value, *cur);
+  grpc_error *err = GRPC_ERROR_CREATE(msg);
+  gpr_free(msg);
+  return parse_error(p, cur, end, err);
 }
 
 /* parse any trailing bytes in a varint: it's possible to append an arbitrary
    number of 0x80's and not affect the value - a zero will terminate - and
    anything else will overflow */
-static int parse_value5up(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                          const uint8_t *end) {
+static grpc_error *parse_value5up(grpc_chttp2_hpack_parser *p,
+                                  const uint8_t *cur, const uint8_t *end) {
   while (cur != end && *cur == 0x80) {
     ++cur;
   }
 
   if (cur == end) {
     p->state = parse_value5up;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   if (*cur == 0) {
     return parse_next(p, cur + 1, end);
   }
 
-  if (grpc_http_trace) {
-    gpr_log(GPR_ERROR,
-            "integer overflow in hpack integer decoding: have 0x%08x, "
-            "got byte 0x%02x sometime after byte 5",
-            *p->parsing.value, *cur);
-  }
-  return parse_error(p, cur, end);
+  char *msg;
+  gpr_asprintf(&msg,
+               "integer overflow in hpack integer decoding: have 0x%08x, "
+               "got byte 0x%02x sometime after byte 5",
+               *p->parsing.value, *cur);
+  grpc_error *err = GRPC_ERROR_CREATE(msg);
+  gpr_free(msg);
+  return parse_error(p, cur, end, err);
 }
 
 /* parse a string prefix */
-static int parse_string_prefix(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                               const uint8_t *end) {
+static grpc_error *parse_string_prefix(grpc_chttp2_hpack_parser *p,
+                                       const uint8_t *cur, const uint8_t *end) {
   if (cur == end) {
     p->state = parse_string_prefix;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   p->strlen = (*cur) & 0x7f;
@@ -1148,25 +1204,26 @@
   str->length += (uint32_t)length;
 }
 
-static int append_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                         const uint8_t *end) {
+static grpc_error *append_string(grpc_chttp2_hpack_parser *p,
+                                 const uint8_t *cur, const uint8_t *end) {
   grpc_chttp2_hpack_parser_string *str = p->parsing.str;
   uint32_t bits;
   uint8_t decoded[3];
   switch ((binary_state)p->binary) {
     case NOT_BINARY:
       append_bytes(str, cur, (size_t)(end - cur));
-      return 1;
+      return GRPC_ERROR_NONE;
     b64_byte0:
     case B64_BYTE0:
       if (cur == end) {
         p->binary = B64_BYTE0;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
       bits = inverse_base64[*cur];
       ++cur;
       if (bits == 255)
-        return 0;
+        return parse_error(p, cur, end,
+                           GRPC_ERROR_CREATE("Illegal base64 character"));
       else if (bits == 64)
         goto b64_byte0;
       p->base64_buffer = bits << 18;
@@ -1175,12 +1232,13 @@
     case B64_BYTE1:
       if (cur == end) {
         p->binary = B64_BYTE1;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
       bits = inverse_base64[*cur];
       ++cur;
       if (bits == 255)
-        return 0;
+        return parse_error(p, cur, end,
+                           GRPC_ERROR_CREATE("Illegal base64 character"));
       else if (bits == 64)
         goto b64_byte1;
       p->base64_buffer |= bits << 12;
@@ -1189,12 +1247,13 @@
     case B64_BYTE2:
       if (cur == end) {
         p->binary = B64_BYTE2;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
       bits = inverse_base64[*cur];
       ++cur;
       if (bits == 255)
-        return 0;
+        return parse_error(p, cur, end,
+                           GRPC_ERROR_CREATE("Illegal base64 character"));
       else if (bits == 64)
         goto b64_byte2;
       p->base64_buffer |= bits << 6;
@@ -1203,12 +1262,13 @@
     case B64_BYTE3:
       if (cur == end) {
         p->binary = B64_BYTE3;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
       bits = inverse_base64[*cur];
       ++cur;
       if (bits == 255)
-        return 0;
+        return parse_error(p, cur, end,
+                           GRPC_ERROR_CREATE("Illegal base64 character"));
       else if (bits == 64)
         goto b64_byte3;
       p->base64_buffer |= bits;
@@ -1219,11 +1279,13 @@
       append_bytes(str, decoded, 3);
       goto b64_byte0;
   }
-  GPR_UNREACHABLE_CODE(return 1);
+  GPR_UNREACHABLE_CODE(return parse_error(
+      p, cur, end, GRPC_ERROR_CREATE("Should never reach here")));
 }
 
 /* append a null terminator to a string */
-static int finish_str(grpc_chttp2_hpack_parser *p) {
+static grpc_error *finish_str(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                              const uint8_t *end) {
   uint8_t terminator = 0;
   uint8_t decoded[2];
   uint32_t bits;
@@ -1234,14 +1296,18 @@
     case B64_BYTE0:
       break;
     case B64_BYTE1:
-      gpr_log(GPR_ERROR, "illegal base64 encoding");
-      return 0; /* illegal encoding */
+      return parse_error(
+          p, cur, end,
+          GRPC_ERROR_CREATE("illegal base64 encoding")); /* illegal encoding */
     case B64_BYTE2:
       bits = p->base64_buffer;
       if (bits & 0xffff) {
-        gpr_log(GPR_ERROR, "trailing bits in base64 encoding: 0x%04x",
-                bits & 0xffff);
-        return 0;
+        char *msg;
+        gpr_asprintf(&msg, "trailing bits in base64 encoding: 0x%04x",
+                     bits & 0xffff);
+        grpc_error *err = GRPC_ERROR_CREATE(msg);
+        gpr_free(msg);
+        return parse_error(p, cur, end, err);
       }
       decoded[0] = (uint8_t)(bits >> 16);
       append_bytes(str, decoded, 1);
@@ -1249,9 +1315,12 @@
     case B64_BYTE3:
       bits = p->base64_buffer;
       if (bits & 0xff) {
-        gpr_log(GPR_ERROR, "trailing bits in base64 encoding: 0x%02x",
-                bits & 0xff);
-        return 0;
+        char *msg;
+        gpr_asprintf(&msg, "trailing bits in base64 encoding: 0x%02x",
+                     bits & 0xff);
+        grpc_error *err = GRPC_ERROR_CREATE(msg);
+        gpr_free(msg);
+        return parse_error(p, cur, end, err);
       }
       decoded[0] = (uint8_t)(bits >> 16);
       decoded[1] = (uint8_t)(bits >> 8);
@@ -1260,38 +1329,42 @@
   }
   append_bytes(str, &terminator, 1);
   p->parsing.str->length--; /* don't actually count the null terminator */
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
 /* decode a nibble from a huffman encoded stream */
-static int huff_nibble(grpc_chttp2_hpack_parser *p, uint8_t nibble) {
+static grpc_error *huff_nibble(grpc_chttp2_hpack_parser *p, uint8_t nibble) {
   int16_t emit = emit_sub_tbl[16 * emit_tbl[p->huff_state] + nibble];
   int16_t next = next_sub_tbl[16 * next_tbl[p->huff_state] + nibble];
   if (emit != -1) {
     if (emit >= 0 && emit < 256) {
       uint8_t c = (uint8_t)emit;
-      if (!append_string(p, &c, (&c) + 1)) return 0;
+      grpc_error *err = append_string(p, &c, (&c) + 1);
+      if (err != GRPC_ERROR_NONE) return err;
     } else {
       assert(emit == 256);
     }
   }
   p->huff_state = next;
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
 /* decode full bytes from a huffman encoded stream */
-static int add_huff_bytes(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                          const uint8_t *end) {
+static grpc_error *add_huff_bytes(grpc_chttp2_hpack_parser *p,
+                                  const uint8_t *cur, const uint8_t *end) {
   for (; cur != end; ++cur) {
-    if (!huff_nibble(p, *cur >> 4) || !huff_nibble(p, *cur & 0xf)) return 0;
+    grpc_error *err = huff_nibble(p, *cur >> 4);
+    if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+    err = huff_nibble(p, *cur & 0xf);
+    if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
   }
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
 /* decode some string bytes based on the current decoding mode
    (huffman or not) */
-static int add_str_bytes(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                         const uint8_t *end) {
+static grpc_error *add_str_bytes(grpc_chttp2_hpack_parser *p,
+                                 const uint8_t *cur, const uint8_t *end) {
   if (p->huff) {
     return add_huff_bytes(p, cur, end);
   } else {
@@ -1300,26 +1373,31 @@
 }
 
 /* parse a string - tries to do large chunks at a time */
-static int parse_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                        const uint8_t *end) {
+static grpc_error *parse_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+                                const uint8_t *end) {
   size_t remaining = p->strlen - p->strgot;
   size_t given = (size_t)(end - cur);
   if (remaining <= given) {
-    return add_str_bytes(p, cur, cur + remaining) && finish_str(p) &&
-           parse_next(p, cur + remaining, end);
+    grpc_error *err = add_str_bytes(p, cur, cur + remaining);
+    if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+    err = finish_str(p, cur + remaining, end);
+    if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+    return parse_next(p, cur + remaining, end);
   } else {
-    if (!add_str_bytes(p, cur, cur + given)) return 0;
+    grpc_error *err = add_str_bytes(p, cur, cur + given);
+    if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
     GPR_ASSERT(given <= UINT32_MAX - p->strgot);
     p->strgot += (uint32_t)given;
     p->state = parse_string;
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 }
 
 /* begin parsing a string - performs setup, calls parse_string */
-static int begin_parse_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                              const uint8_t *end, uint8_t binary,
-                              grpc_chttp2_hpack_parser_string *str) {
+static grpc_error *begin_parse_string(grpc_chttp2_hpack_parser *p,
+                                      const uint8_t *cur, const uint8_t *end,
+                                      uint8_t binary,
+                                      grpc_chttp2_hpack_parser_string *str) {
   p->strgot = 0;
   str->length = 0;
   p->parsing.str = str;
@@ -1329,58 +1407,50 @@
 }
 
 /* parse the key string */
-static int parse_key_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                            const uint8_t *end) {
+static grpc_error *parse_key_string(grpc_chttp2_hpack_parser *p,
+                                    const uint8_t *cur, const uint8_t *end) {
   return begin_parse_string(p, cur, end, NOT_BINARY, &p->key);
 }
 
 /* check if a key represents a binary header or not */
-typedef enum { BINARY_HEADER, PLAINTEXT_HEADER, ERROR_HEADER } is_binary_header;
 
-static is_binary_header is_binary_literal_header(grpc_chttp2_hpack_parser *p) {
-  return grpc_is_binary_header(p->key.str, p->key.length) ? BINARY_HEADER
-                                                          : PLAINTEXT_HEADER;
+static bool is_binary_literal_header(grpc_chttp2_hpack_parser *p) {
+  return grpc_is_binary_header(p->key.str, p->key.length);
 }
 
-static is_binary_header is_binary_indexed_header(grpc_chttp2_hpack_parser *p) {
+static grpc_error *is_binary_indexed_header(grpc_chttp2_hpack_parser *p,
+                                            bool *is) {
   grpc_mdelem *elem = grpc_chttp2_hptbl_lookup(&p->table, p->index);
   if (!elem) {
-    if (grpc_http_trace) {
-      gpr_log(GPR_ERROR, "Invalid HPACK index received: %d", p->index);
-    }
-    return ERROR_HEADER;
+    return grpc_error_set_int(
+        grpc_error_set_int(GRPC_ERROR_CREATE("Invalid HPACK index received"),
+                           GRPC_ERROR_INT_INDEX, p->index),
+        GRPC_ERROR_INT_SIZE, p->table.num_ents);
   }
-  return grpc_is_binary_header(
-             (const char *)GPR_SLICE_START_PTR(elem->key->slice),
-             GPR_SLICE_LENGTH(elem->key->slice))
-             ? BINARY_HEADER
-             : PLAINTEXT_HEADER;
+  *is =
+      grpc_is_binary_header((const char *)GPR_SLICE_START_PTR(elem->key->slice),
+                            GPR_SLICE_LENGTH(elem->key->slice));
+  return GRPC_ERROR_NONE;
 }
 
 /* parse the value string */
-static int parse_value_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
-                              const uint8_t *end, is_binary_header type) {
-  switch (type) {
-    case BINARY_HEADER:
-      return begin_parse_string(p, cur, end, B64_BYTE0, &p->value);
-    case PLAINTEXT_HEADER:
-      return begin_parse_string(p, cur, end, NOT_BINARY, &p->value);
-    case ERROR_HEADER:
-      return 0;
-  }
-  /* Add code to prevent return without value error */
-  GPR_UNREACHABLE_CODE(return 0);
+static grpc_error *parse_value_string(grpc_chttp2_hpack_parser *p,
+                                      const uint8_t *cur, const uint8_t *end,
+                                      bool is_binary) {
+  return begin_parse_string(p, cur, end, is_binary ? B64_BYTE0 : NOT_BINARY,
+                            &p->value);
 }
 
-static int parse_value_string_with_indexed_key(grpc_chttp2_hpack_parser *p,
-                                               const uint8_t *cur,
-                                               const uint8_t *end) {
-  return parse_value_string(p, cur, end, is_binary_indexed_header(p));
+static grpc_error *parse_value_string_with_indexed_key(
+    grpc_chttp2_hpack_parser *p, const uint8_t *cur, const uint8_t *end) {
+  bool is_binary = false;
+  grpc_error *err = is_binary_indexed_header(p, &is_binary);
+  if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err);
+  return parse_value_string(p, cur, end, is_binary);
 }
 
-static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p,
-                                               const uint8_t *cur,
-                                               const uint8_t *end) {
+static grpc_error *parse_value_string_with_literal_key(
+    grpc_chttp2_hpack_parser *p, const uint8_t *cur, const uint8_t *end) {
   return parse_value_string(p, cur, end, is_binary_literal_header(p));
 }
 
@@ -1397,6 +1467,7 @@
   p->value.capacity = 0;
   p->value.length = 0;
   p->dynamic_table_update_allowed = 2;
+  p->last_error = GRPC_ERROR_NONE;
   grpc_chttp2_hptbl_init(&p->table);
 }
 
@@ -1407,12 +1478,14 @@
 
 void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p) {
   grpc_chttp2_hptbl_destroy(&p->table);
+  GRPC_ERROR_UNREF(p->last_error);
   gpr_free(p->key.str);
   gpr_free(p->value.str);
 }
 
-int grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p,
-                                   const uint8_t *beg, const uint8_t *end) {
+grpc_error *grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p,
+                                           const uint8_t *beg,
+                                           const uint8_t *end) {
   /* TODO(ctiller): limit the distance of end from beg, and perform multiple
      steps in the event of a large chunk of data to limit
      stack space usage when no tail call optimization is
@@ -1420,7 +1493,7 @@
   return p->state(p, beg, end);
 }
 
-grpc_chttp2_parse_error grpc_chttp2_header_parser_parse(
+grpc_error *grpc_chttp2_header_parser_parse(
     grpc_exec_ctx *exec_ctx, void *hpack_parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) {
@@ -1429,17 +1502,17 @@
   if (stream_parsing != NULL) {
     stream_parsing->stats.incoming.header_bytes += GPR_SLICE_LENGTH(slice);
   }
-  if (!grpc_chttp2_hpack_parser_parse(parser, GPR_SLICE_START_PTR(slice),
-                                      GPR_SLICE_END_PTR(slice))) {
+  grpc_error *error = grpc_chttp2_hpack_parser_parse(
+      parser, GPR_SLICE_START_PTR(slice), GPR_SLICE_END_PTR(slice));
+  if (error != GRPC_ERROR_NONE) {
     GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0);
-    return GRPC_CHTTP2_CONNECTION_ERROR;
+    return error;
   }
   if (is_last) {
     if (parser->is_boundary && parser->state != parse_begin) {
-      gpr_log(GPR_ERROR,
-              "end of header frame not aligned with a hpack record boundary");
       GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0);
-      return GRPC_CHTTP2_CONNECTION_ERROR;
+      return GRPC_ERROR_CREATE(
+          "end of header frame not aligned with a hpack record boundary");
     }
     /* need to check for null stream: this can occur if we receive an invalid
        stream id on a header */
@@ -1462,5 +1535,5 @@
     parser->dynamic_table_update_allowed = 2;
   }
   GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0);
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.h b/src/core/ext/transport/chttp2/transport/hpack_parser.h
index 855d6c5..78eb38d 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_parser.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_parser.h
@@ -44,9 +44,8 @@
 
 typedef struct grpc_chttp2_hpack_parser grpc_chttp2_hpack_parser;
 
-typedef int (*grpc_chttp2_hpack_parser_state)(grpc_chttp2_hpack_parser *p,
-                                              const uint8_t *beg,
-                                              const uint8_t *end);
+typedef grpc_error *(*grpc_chttp2_hpack_parser_state)(
+    grpc_chttp2_hpack_parser *p, const uint8_t *beg, const uint8_t *end);
 
 typedef struct {
   char *str;
@@ -59,6 +58,8 @@
   void (*on_header)(void *user_data, grpc_mdelem *md);
   void *on_header_user_data;
 
+  grpc_error *last_error;
+
   /* current parse state - or a function that implements it */
   grpc_chttp2_hpack_parser_state state;
   /* future states dependent on the opening op code */
@@ -103,12 +104,13 @@
 void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p);
 
 /* returns 1 on success, 0 on error */
-int grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p,
-                                   const uint8_t *beg, const uint8_t *end);
+grpc_error *grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p,
+                                           const uint8_t *beg,
+                                           const uint8_t *end);
 
 /* wraps grpc_chttp2_hpack_parser_parse to provide a frame level parser for
    the transport */
-grpc_chttp2_parse_error grpc_chttp2_header_parser_parse(
+grpc_error *grpc_chttp2_header_parser_parse(
     grpc_exec_ctx *exec_ctx, void *hpack_parser,
     grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last);
diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.c b/src/core/ext/transport/chttp2/transport/hpack_table.c
index 295f31c..2b73ec9 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_table.c
+++ b/src/core/ext/transport/chttp2/transport/hpack_table.c
@@ -38,6 +38,7 @@
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
 
 #include "src/core/lib/support/murmur_hash.h"
 
@@ -262,18 +263,19 @@
   tbl->max_bytes = max_bytes;
 }
 
-int grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
-                                             uint32_t bytes) {
+grpc_error *grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
+                                                     uint32_t bytes) {
   if (tbl->current_table_bytes == bytes) {
-    return 1;
+    return GRPC_ERROR_NONE;
   }
   if (bytes > tbl->max_bytes) {
-    if (grpc_http_trace) {
-      gpr_log(GPR_ERROR,
-              "Attempt to make hpack table %d bytes when max is %d bytes",
-              bytes, tbl->max_bytes);
-    }
-    return 0;
+    char *msg;
+    gpr_asprintf(&msg,
+                 "Attempt to make hpack table %d bytes when max is %d bytes",
+                 bytes, tbl->max_bytes);
+    grpc_error *err = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+    return err;
   }
   if (grpc_http_trace) {
     gpr_log(GPR_DEBUG, "Update hpack parser table size to %d", bytes);
@@ -291,23 +293,25 @@
       rebuild_ents(tbl, new_cap);
     }
   }
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
-int grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
+grpc_error *grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
   /* determine how many bytes of buffer this entry represents */
   size_t elem_bytes = GPR_SLICE_LENGTH(md->key->slice) +
                       GPR_SLICE_LENGTH(md->value->slice) +
                       GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
 
   if (tbl->current_table_bytes > tbl->max_bytes) {
-    if (grpc_http_trace) {
-      gpr_log(GPR_ERROR,
-              "HPACK max table size reduced to %d but not reflected by hpack "
-              "stream (still at %d)",
-              tbl->max_bytes, tbl->current_table_bytes);
-    }
-    return 0;
+    char *msg;
+    gpr_asprintf(
+        &msg,
+        "HPACK max table size reduced to %d but not reflected by hpack "
+        "stream (still at %d)",
+        tbl->max_bytes, tbl->current_table_bytes);
+    grpc_error *err = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+    return err;
   }
 
   /* we can't add elements bigger than the max table size */
@@ -324,7 +328,7 @@
     while (tbl->num_ents) {
       evict1(tbl);
     }
-    return 1;
+    return GRPC_ERROR_NONE;
   }
 
   /* evict entries to ensure no overflow */
@@ -339,7 +343,7 @@
   /* update accounting values */
   tbl->num_ents++;
   tbl->mem_used += (uint32_t)elem_bytes;
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
 grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.h b/src/core/ext/transport/chttp2/transport/hpack_table.h
index 074fea3..45bd925 100644
--- a/src/core/ext/transport/chttp2/transport/hpack_table.h
+++ b/src/core/ext/transport/chttp2/transport/hpack_table.h
@@ -36,6 +36,7 @@
 
 #include <grpc/support/port_platform.h>
 #include <grpc/support/slice.h>
+#include "src/core/lib/iomgr/error.h"
 #include "src/core/lib/transport/metadata.h"
 
 /* HPACK header table */
@@ -87,15 +88,15 @@
 void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl);
 void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl,
                                      uint32_t max_bytes);
-int grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
-                                             uint32_t bytes);
+grpc_error *grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
+                                                     uint32_t bytes);
 
 /* lookup a table entry based on its hpack index */
 grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
                                       uint32_t index);
 /* add a table entry to the index */
-int grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl,
-                          grpc_mdelem *md) GRPC_MUST_USE_RESULT;
+grpc_error *grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl,
+                                  grpc_mdelem *md) GRPC_MUST_USE_RESULT;
 /* Find a key/value pair in the table... returns the index in the table of the
    most similar entry, or 0 if the value was not found */
 typedef struct {
diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h
index 5872fd8..7e5d583 100644
--- a/src/core/ext/transport/chttp2/transport/internal.h
+++ b/src/core/ext/transport/chttp2/transport/internal.h
@@ -156,7 +156,7 @@
   grpc_byte_stream base;
   gpr_refcount refs;
   struct grpc_chttp2_incoming_byte_stream *next_message;
-  int failed;
+  grpc_error *error;
 
   grpc_chttp2_transport *transport;
   grpc_chttp2_stream *stream;
@@ -275,10 +275,10 @@
   /* active parser */
   void *parser_data;
   grpc_chttp2_stream_parsing *incoming_stream;
-  grpc_chttp2_parse_error (*parser)(
-      grpc_exec_ctx *exec_ctx, void *parser_user_data,
-      grpc_chttp2_transport_parsing *transport_parsing,
-      grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last);
+  grpc_error *(*parser)(grpc_exec_ctx *exec_ctx, void *parser_user_data,
+                        grpc_chttp2_transport_parsing *transport_parsing,
+                        grpc_chttp2_stream_parsing *stream_parsing,
+                        gpr_slice slice, int is_last);
 
   /* received settings */
   uint32_t settings[GRPC_CHTTP2_NUM_SETTINGS];
@@ -468,12 +468,12 @@
 } grpc_chttp2_stream_writing;
 
 struct grpc_chttp2_stream_parsing {
+  /** saw some stream level error */
+  grpc_error *forced_close_error;
   /** HTTP2 stream id for this stream, or zero if one has not been assigned */
   uint32_t id;
   /** has this stream received a close */
   uint8_t received_close;
-  /** saw a rst_stream */
-  uint8_t saw_rst_stream;
   /** how many header frames have we received? */
   uint8_t header_frames_received;
   /** which metadata did we get (on this parse) */
@@ -485,8 +485,6 @@
   int64_t incoming_window;
   /** parsing state for data frames */
   grpc_chttp2_data_parser data_parser;
-  /** reason give to rst_stream */
-  uint32_t rst_stream_reason;
   /** amount of window given */
   int64_t outgoing_window;
   /** number of bytes received - reset at end of parse thread execution */
@@ -529,7 +527,7 @@
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_writing *transport_writing,
     grpc_endpoint *endpoint);
 void grpc_chttp2_terminate_writing(grpc_exec_ctx *exec_ctx,
-                                   void *transport_writing, bool success);
+                                   void *transport_writing, grpc_error *error);
 void grpc_chttp2_cleanup_writing(grpc_exec_ctx *exec_ctx,
                                  grpc_chttp2_transport_global *global,
                                  grpc_chttp2_transport_writing *writing);
@@ -538,9 +536,9 @@
                                  grpc_chttp2_transport_parsing *parsing);
 /** Process one slice of incoming data; return 1 if the connection is still
     viable after reading, or 0 if the connection should be torn down */
-int grpc_chttp2_perform_read(grpc_exec_ctx *exec_ctx,
-                             grpc_chttp2_transport_parsing *transport_parsing,
-                             gpr_slice slice);
+grpc_error *grpc_chttp2_perform_read(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing,
+    gpr_slice slice);
 void grpc_chttp2_publish_reads(grpc_exec_ctx *exec_ctx,
                                grpc_chttp2_transport_global *global,
                                grpc_chttp2_transport_parsing *parsing);
@@ -670,9 +668,10 @@
 void grpc_chttp2_parsing_become_skip_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing);
 
-void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx,
-                                       grpc_chttp2_stream_global *stream_global,
-                                       grpc_closure **pclosure, int success);
+void grpc_chttp2_complete_closure_step(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
+    grpc_chttp2_stream_global *stream_global, grpc_closure **pclosure,
+    grpc_error *error);
 
 void grpc_chttp2_run_with_global_lock(grpc_exec_ctx *exec_ctx,
                                       grpc_chttp2_transport *transport,
@@ -775,8 +774,8 @@
                              grpc_status_code status, gpr_slice *details);
 void grpc_chttp2_mark_stream_closed(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
-    grpc_chttp2_stream_global *stream_global, int close_reads,
-    int close_writes);
+    grpc_chttp2_stream_global *stream_global, int close_reads, int close_writes,
+    grpc_error *error);
 void grpc_chttp2_start_writing(grpc_exec_ctx *exec_ctx,
                                grpc_chttp2_transport_global *transport_global);
 
@@ -808,8 +807,8 @@
                                            grpc_chttp2_incoming_byte_stream *bs,
                                            gpr_slice slice);
 void grpc_chttp2_incoming_byte_stream_finished(
-    grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, int success,
-    int from_parsing_thread);
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs,
+    grpc_error *error, int from_parsing_thread);
 
 void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx,
                           grpc_chttp2_transport_parsing *parsing,
diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c
index 4bd374b..72b3131 100644
--- a/src/core/ext/transport/chttp2/transport/parsing.c
+++ b/src/core/ext/transport/chttp2/transport/parsing.c
@@ -49,30 +49,30 @@
   ((grpc_chttp2_transport *)((char *)(tp)-offsetof(grpc_chttp2_transport, \
                                                    parsing)))
 
-static int init_frame_parser(grpc_exec_ctx *exec_ctx,
-                             grpc_chttp2_transport_parsing *transport_parsing);
-static int init_header_frame_parser(
+static grpc_error *init_frame_parser(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing);
+static grpc_error *init_header_frame_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing,
     int is_continuation);
-static int init_data_frame_parser(
+static grpc_error *init_data_frame_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing);
-static int init_rst_stream_parser(
+static grpc_error *init_rst_stream_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing);
-static int init_settings_frame_parser(
+static grpc_error *init_settings_frame_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing);
-static int init_window_update_frame_parser(
+static grpc_error *init_window_update_frame_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing);
-static int init_ping_parser(grpc_exec_ctx *exec_ctx,
-                            grpc_chttp2_transport_parsing *transport_parsing);
-static int init_goaway_parser(grpc_exec_ctx *exec_ctx,
-                              grpc_chttp2_transport_parsing *transport_parsing);
-static int init_skip_frame_parser(
+static grpc_error *init_ping_parser(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing);
+static grpc_error *init_goaway_parser(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing);
+static grpc_error *init_skip_frame_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing,
     int is_header);
 
-static int parse_frame_slice(grpc_exec_ctx *exec_ctx,
-                             grpc_chttp2_transport_parsing *transport_parsing,
-                             gpr_slice slice, int is_last);
+static grpc_error *parse_frame_slice(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing,
+    gpr_slice slice, int is_last);
 
 void grpc_chttp2_prepare_to_read(
     grpc_chttp2_transport_global *transport_global,
@@ -230,38 +230,42 @@
       grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
     }
 
-    if (stream_parsing->saw_rst_stream) {
-      if (stream_parsing->rst_stream_reason != GRPC_CHTTP2_NO_ERROR) {
-        grpc_status_code status_code = grpc_chttp2_http2_error_to_grpc_status(
-            (grpc_chttp2_error_code)stream_parsing->rst_stream_reason);
-        char *status_details;
-        gpr_slice slice_details;
-        gpr_asprintf(&status_details, "Received RST_STREAM err=%d",
-                     stream_parsing->rst_stream_reason);
-        slice_details = gpr_slice_from_copied_string(status_details);
-        gpr_free(status_details);
+    if (stream_parsing->forced_close_error != GRPC_ERROR_NONE) {
+      intptr_t reason;
+      bool has_reason = grpc_error_get_int(stream_parsing->forced_close_error,
+                                           GRPC_ERROR_INT_HTTP2_ERROR, &reason);
+      if (has_reason && reason != GRPC_CHTTP2_NO_ERROR) {
+        grpc_status_code status_code =
+            has_reason ? grpc_chttp2_http2_error_to_grpc_status(
+                             (grpc_chttp2_error_code)reason)
+                       : GRPC_STATUS_INTERNAL;
+        const char *status_details =
+            grpc_error_string(stream_parsing->forced_close_error);
+        gpr_slice slice_details = gpr_slice_from_copied_string(status_details);
+        grpc_error_free_string(status_details);
         grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global,
                                 status_code, &slice_details);
       }
       grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global,
-                                     1, 1);
+                                     1, 1, stream_parsing->forced_close_error);
     }
 
     if (stream_parsing->received_close) {
       grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global,
-                                     1, 0);
+                                     1, 0, GRPC_ERROR_NONE);
     }
   }
 }
 
-int grpc_chttp2_perform_read(grpc_exec_ctx *exec_ctx,
-                             grpc_chttp2_transport_parsing *transport_parsing,
-                             gpr_slice slice) {
+grpc_error *grpc_chttp2_perform_read(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing,
+    gpr_slice slice) {
   uint8_t *beg = GPR_SLICE_START_PTR(slice);
   uint8_t *end = GPR_SLICE_END_PTR(slice);
   uint8_t *cur = beg;
+  grpc_error *err;
 
-  if (cur == end) return 1;
+  if (cur == end) return GRPC_ERROR_NONE;
 
   switch (transport_parsing->deframe_state) {
     case GRPC_DTS_CLIENT_PREFIX_0:
@@ -291,21 +295,25 @@
       while (cur != end && transport_parsing->deframe_state != GRPC_DTS_FH_0) {
         if (*cur != GRPC_CHTTP2_CLIENT_CONNECT_STRING[transport_parsing
                                                           ->deframe_state]) {
-          gpr_log(GPR_INFO,
-                  "Connect string mismatch: expected '%c' (%d) got '%c' (%d) "
-                  "at byte %d",
-                  GRPC_CHTTP2_CLIENT_CONNECT_STRING[transport_parsing
-                                                        ->deframe_state],
-                  (int)(uint8_t)GRPC_CHTTP2_CLIENT_CONNECT_STRING
-                      [transport_parsing->deframe_state],
-                  *cur, (int)*cur, transport_parsing->deframe_state);
-          return 0;
+          char *msg;
+          gpr_asprintf(
+              &msg,
+              "Connect string mismatch: expected '%c' (%d) got '%c' (%d) "
+              "at byte %d",
+              GRPC_CHTTP2_CLIENT_CONNECT_STRING[transport_parsing
+                                                    ->deframe_state],
+              (int)(uint8_t)GRPC_CHTTP2_CLIENT_CONNECT_STRING
+                  [transport_parsing->deframe_state],
+              *cur, (int)*cur, transport_parsing->deframe_state);
+          err = GRPC_ERROR_CREATE(msg);
+          gpr_free(msg);
+          return err;
         }
         ++cur;
         ++transport_parsing->deframe_state;
       }
       if (cur == end) {
-        return 1;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     dts_fh_0:
@@ -314,7 +322,7 @@
       transport_parsing->incoming_frame_size = ((uint32_t)*cur) << 16;
       if (++cur == end) {
         transport_parsing->deframe_state = GRPC_DTS_FH_1;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_DTS_FH_1:
@@ -322,7 +330,7 @@
       transport_parsing->incoming_frame_size |= ((uint32_t)*cur) << 8;
       if (++cur == end) {
         transport_parsing->deframe_state = GRPC_DTS_FH_2;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_DTS_FH_2:
@@ -330,7 +338,7 @@
       transport_parsing->incoming_frame_size |= *cur;
       if (++cur == end) {
         transport_parsing->deframe_state = GRPC_DTS_FH_3;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_DTS_FH_3:
@@ -338,7 +346,7 @@
       transport_parsing->incoming_frame_type = *cur;
       if (++cur == end) {
         transport_parsing->deframe_state = GRPC_DTS_FH_4;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_DTS_FH_4:
@@ -346,7 +354,7 @@
       transport_parsing->incoming_frame_flags = *cur;
       if (++cur == end) {
         transport_parsing->deframe_state = GRPC_DTS_FH_5;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_DTS_FH_5:
@@ -354,7 +362,7 @@
       transport_parsing->incoming_stream_id = (((uint32_t)*cur) & 0x7f) << 24;
       if (++cur == end) {
         transport_parsing->deframe_state = GRPC_DTS_FH_6;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_DTS_FH_6:
@@ -362,7 +370,7 @@
       transport_parsing->incoming_stream_id |= ((uint32_t)*cur) << 16;
       if (++cur == end) {
         transport_parsing->deframe_state = GRPC_DTS_FH_7;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_DTS_FH_7:
@@ -370,15 +378,16 @@
       transport_parsing->incoming_stream_id |= ((uint32_t)*cur) << 8;
       if (++cur == end) {
         transport_parsing->deframe_state = GRPC_DTS_FH_8;
-        return 1;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_DTS_FH_8:
       GPR_ASSERT(cur < end);
       transport_parsing->incoming_stream_id |= ((uint32_t)*cur);
       transport_parsing->deframe_state = GRPC_DTS_FRAME;
-      if (!init_frame_parser(exec_ctx, transport_parsing)) {
-        return 0;
+      err = init_frame_parser(exec_ctx, transport_parsing);
+      if (err != GRPC_ERROR_NONE) {
+        return err;
       }
       if (transport_parsing->incoming_stream_id != 0 &&
           transport_parsing->incoming_stream_id >
@@ -387,62 +396,69 @@
             transport_parsing->incoming_stream_id;
       }
       if (transport_parsing->incoming_frame_size == 0) {
-        if (!parse_frame_slice(exec_ctx, transport_parsing, gpr_empty_slice(),
-                               1)) {
-          return 0;
+        err = parse_frame_slice(exec_ctx, transport_parsing, gpr_empty_slice(),
+                                1);
+        if (err != GRPC_ERROR_NONE) {
+          return err;
         }
         transport_parsing->incoming_stream = NULL;
         if (++cur == end) {
           transport_parsing->deframe_state = GRPC_DTS_FH_0;
-          return 1;
+          return GRPC_ERROR_NONE;
         }
         goto dts_fh_0; /* loop */
       } else if (transport_parsing->incoming_frame_size >
                  transport_parsing->max_frame_size) {
-        gpr_log(GPR_DEBUG, "Frame size %d is larger than max frame size %d",
-                transport_parsing->incoming_frame_size,
-                transport_parsing->max_frame_size);
-        return 0;
+        char *msg;
+        gpr_asprintf(&msg, "Frame size %d is larger than max frame size %d",
+                     transport_parsing->incoming_frame_size,
+                     transport_parsing->max_frame_size);
+        err = GRPC_ERROR_CREATE(msg);
+        gpr_free(msg);
+        return err;
       }
       if (++cur == end) {
-        return 1;
+        return GRPC_ERROR_NONE;
       }
     /* fallthrough */
     case GRPC_DTS_FRAME:
       GPR_ASSERT(cur < end);
       if ((uint32_t)(end - cur) == transport_parsing->incoming_frame_size) {
-        if (!parse_frame_slice(exec_ctx, transport_parsing,
-                               gpr_slice_sub_no_ref(slice, (size_t)(cur - beg),
-                                                    (size_t)(end - beg)),
-                               1)) {
-          return 0;
+        err = parse_frame_slice(exec_ctx, transport_parsing,
+                                gpr_slice_sub_no_ref(slice, (size_t)(cur - beg),
+                                                     (size_t)(end - beg)),
+                                1);
+        if (err != GRPC_ERROR_NONE) {
+          return err;
         }
         transport_parsing->deframe_state = GRPC_DTS_FH_0;
         transport_parsing->incoming_stream = NULL;
-        return 1;
+        return GRPC_ERROR_NONE;
       } else if ((uint32_t)(end - cur) >
                  transport_parsing->incoming_frame_size) {
         size_t cur_offset = (size_t)(cur - beg);
-        if (!parse_frame_slice(
-                exec_ctx, transport_parsing,
-                gpr_slice_sub_no_ref(
-                    slice, cur_offset,
-                    cur_offset + transport_parsing->incoming_frame_size),
-                1)) {
-          return 0;
+        err = parse_frame_slice(
+            exec_ctx, transport_parsing,
+            gpr_slice_sub_no_ref(
+                slice, cur_offset,
+                cur_offset + transport_parsing->incoming_frame_size),
+            1);
+        if (err != GRPC_ERROR_NONE) {
+          return err;
         }
         cur += transport_parsing->incoming_frame_size;
         transport_parsing->incoming_stream = NULL;
         goto dts_fh_0; /* loop */
       } else {
-        if (!parse_frame_slice(exec_ctx, transport_parsing,
-                               gpr_slice_sub_no_ref(slice, (size_t)(cur - beg),
-                                                    (size_t)(end - beg)),
-                               0)) {
-          return 0;
+        err = parse_frame_slice(exec_ctx, transport_parsing,
+                                gpr_slice_sub_no_ref(slice, (size_t)(cur - beg),
+                                                     (size_t)(end - beg)),
+                                0);
+        if (err != GRPC_ERROR_NONE) {
+          return err;
         }
         transport_parsing->incoming_frame_size -= (uint32_t)(end - cur);
-        return 1;
+        return GRPC_ERROR_NONE;
       }
       GPR_UNREACHABLE_CODE(return 0);
   }
@@ -450,23 +466,30 @@
   GPR_UNREACHABLE_CODE(return 0);
 }
 
-static int init_frame_parser(grpc_exec_ctx *exec_ctx,
-                             grpc_chttp2_transport_parsing *transport_parsing) {
+static grpc_error *init_frame_parser(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing) {
   if (transport_parsing->expect_continuation_stream_id != 0) {
     if (transport_parsing->incoming_frame_type !=
         GRPC_CHTTP2_FRAME_CONTINUATION) {
-      gpr_log(GPR_ERROR, "Expected CONTINUATION frame, got frame type %02x",
-              transport_parsing->incoming_frame_type);
-      return 0;
+      char *msg;
+      gpr_asprintf(&msg, "Expected CONTINUATION frame, got frame type %02x",
+                   transport_parsing->incoming_frame_type);
+      grpc_error *err = GRPC_ERROR_CREATE(msg);
+      gpr_free(msg);
+      return err;
     }
     if (transport_parsing->expect_continuation_stream_id !=
         transport_parsing->incoming_stream_id) {
-      gpr_log(GPR_ERROR,
-              "Expected CONTINUATION frame for grpc_chttp2_stream %08x, got "
-              "grpc_chttp2_stream %08x",
-              transport_parsing->expect_continuation_stream_id,
-              transport_parsing->incoming_stream_id);
-      return 0;
+      char *msg;
+      gpr_asprintf(
+          &msg,
+          "Expected CONTINUATION frame for grpc_chttp2_stream %08x, got "
+          "grpc_chttp2_stream %08x",
+          transport_parsing->expect_continuation_stream_id,
+          transport_parsing->incoming_stream_id);
+      grpc_error *err = GRPC_ERROR_CREATE(msg);
+      gpr_free(msg);
+      return err;
     }
     return init_header_frame_parser(exec_ctx, transport_parsing, 1);
   }
@@ -476,8 +499,7 @@
     case GRPC_CHTTP2_FRAME_HEADER:
       return init_header_frame_parser(exec_ctx, transport_parsing, 0);
     case GRPC_CHTTP2_FRAME_CONTINUATION:
-      gpr_log(GPR_ERROR, "Unexpected CONTINUATION frame");
-      return 0;
+      return GRPC_ERROR_CREATE("Unexpected CONTINUATION frame");
     case GRPC_CHTTP2_FRAME_RST_STREAM:
       return init_rst_stream_parser(exec_ctx, transport_parsing);
     case GRPC_CHTTP2_FRAME_SETTINGS:
@@ -489,22 +511,24 @@
     case GRPC_CHTTP2_FRAME_GOAWAY:
       return init_goaway_parser(exec_ctx, transport_parsing);
     default:
-      gpr_log(GPR_ERROR, "Unknown frame type %02x",
-              transport_parsing->incoming_frame_type);
+      if (grpc_http_trace) {
+        gpr_log(GPR_ERROR, "Unknown frame type %02x",
+                transport_parsing->incoming_frame_type);
+      }
       return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
   }
 }
 
-static grpc_chttp2_parse_error skip_parser(
-    grpc_exec_ctx *exec_ctx, void *parser,
-    grpc_chttp2_transport_parsing *transport_parsing,
-    grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) {
-  return GRPC_CHTTP2_PARSE_OK;
+static grpc_error *skip_parser(grpc_exec_ctx *exec_ctx, void *parser,
+                               grpc_chttp2_transport_parsing *transport_parsing,
+                               grpc_chttp2_stream_parsing *stream_parsing,
+                               gpr_slice slice, int is_last) {
+  return GRPC_ERROR_NONE;
 }
 
 static void skip_header(void *tp, grpc_mdelem *md) { GRPC_MDELEM_UNREF(md); }
 
-static int init_skip_frame_parser(
+static grpc_error *init_skip_frame_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing,
     int is_header) {
   if (is_header) {
@@ -519,7 +543,7 @@
   } else {
     transport_parsing->parser = skip_parser;
   }
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
 void grpc_chttp2_parsing_become_skip_parser(
@@ -529,22 +553,28 @@
       transport_parsing->parser == grpc_chttp2_header_parser_parse);
 }
 
-static grpc_chttp2_parse_error update_incoming_window(
+static grpc_error *update_incoming_window(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing,
     grpc_chttp2_stream_parsing *stream_parsing) {
   uint32_t incoming_frame_size = transport_parsing->incoming_frame_size;
   if (incoming_frame_size > transport_parsing->incoming_window) {
-    gpr_log(GPR_ERROR, "frame of size %d overflows incoming window of %d",
-            transport_parsing->incoming_frame_size,
-            transport_parsing->incoming_window);
-    return GRPC_CHTTP2_CONNECTION_ERROR;
+    char *msg;
+    gpr_asprintf(&msg, "frame of size %d overflows incoming window of %d",
+                 transport_parsing->incoming_frame_size,
+                 transport_parsing->incoming_window);
+    grpc_error *err = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+    return err;
   }
 
   if (incoming_frame_size > stream_parsing->incoming_window) {
-    gpr_log(GPR_ERROR, "frame of size %d overflows incoming window of %d",
-            transport_parsing->incoming_frame_size,
-            stream_parsing->incoming_window);
-    return GRPC_CHTTP2_CONNECTION_ERROR;
+    char *msg;
+    gpr_asprintf(&msg, "frame of size %d overflows incoming window of %d",
+                 transport_parsing->incoming_frame_size,
+                 stream_parsing->incoming_window);
+    grpc_error *err = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
+    return err;
   }
 
   GRPC_CHTTP2_FLOW_DEBIT_TRANSPORT("parse", transport_parsing, incoming_window,
@@ -555,15 +585,15 @@
 
   grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, stream_parsing);
 
-  return GRPC_CHTTP2_PARSE_OK;
+  return GRPC_ERROR_NONE;
 }
 
-static int init_data_frame_parser(
+static grpc_error *init_data_frame_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing) {
   grpc_chttp2_stream_parsing *stream_parsing =
       grpc_chttp2_parsing_lookup_stream(transport_parsing,
                                         transport_parsing->incoming_stream_id);
-  grpc_chttp2_parse_error err = GRPC_CHTTP2_PARSE_OK;
+  grpc_error *err = GRPC_ERROR_NONE;
   if (stream_parsing == NULL) {
     return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
   }
@@ -571,33 +601,32 @@
   if (stream_parsing->received_close) {
     return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
   }
-  if (err == GRPC_CHTTP2_PARSE_OK) {
+  if (err == GRPC_ERROR_NONE) {
     err = update_incoming_window(exec_ctx, transport_parsing, stream_parsing);
   }
-  if (err == GRPC_CHTTP2_PARSE_OK) {
+  if (err == GRPC_ERROR_NONE) {
     err = grpc_chttp2_data_parser_begin_frame(
-        &stream_parsing->data_parser, transport_parsing->incoming_frame_flags);
+        &stream_parsing->data_parser, transport_parsing->incoming_frame_flags,
+        stream_parsing->id);
   }
-  switch (err) {
-    case GRPC_CHTTP2_PARSE_OK:
-      transport_parsing->incoming_stream = stream_parsing;
-      transport_parsing->parser = grpc_chttp2_data_parser_parse;
-      transport_parsing->parser_data = &stream_parsing->data_parser;
-      return 1;
-    case GRPC_CHTTP2_STREAM_ERROR:
-      stream_parsing->received_close = 1;
-      stream_parsing->saw_rst_stream = 1;
-      stream_parsing->rst_stream_reason = GRPC_CHTTP2_PROTOCOL_ERROR;
-      gpr_slice_buffer_add(
-          &transport_parsing->qbuf,
-          grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id,
-                                        GRPC_CHTTP2_PROTOCOL_ERROR,
-                                        &stream_parsing->stats.outgoing));
-      return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
-    case GRPC_CHTTP2_CONNECTION_ERROR:
-      return 0;
+  if (err == GRPC_ERROR_NONE) {
+    transport_parsing->incoming_stream = stream_parsing;
+    transport_parsing->parser = grpc_chttp2_data_parser_parse;
+    transport_parsing->parser_data = &stream_parsing->data_parser;
+    return GRPC_ERROR_NONE;
+  } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) {
+    /* handle stream errors by closing the stream */
+    stream_parsing->received_close = 1;
+    stream_parsing->forced_close_error = err;
+    gpr_slice_buffer_add(
+        &transport_parsing->qbuf,
+        grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id,
+                                      GRPC_CHTTP2_PROTOCOL_ERROR,
+                                      &stream_parsing->stats.outgoing));
+    return init_skip_frame_parser(exec_ctx, transport_parsing, 0);
+  } else {
+    return err;
   }
-  GPR_UNREACHABLE_CODE(return 0);
 }
 
 static void free_timeout(void *p) { gpr_free(p); }
@@ -711,7 +740,7 @@
   GPR_TIMER_END("on_trailing_header", 0);
 }
 
-static int init_header_frame_parser(
+static grpc_error *init_header_frame_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing,
     int is_continuation) {
   uint8_t is_eoh = (transport_parsing->incoming_frame_flags &
@@ -806,15 +835,16 @@
                            GRPC_CHTTP2_FLAG_HAS_PRIORITY)) {
     grpc_chttp2_hpack_parser_set_has_priority(&transport_parsing->hpack_parser);
   }
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
-static int init_window_update_frame_parser(
+static grpc_error *init_window_update_frame_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing) {
-  int ok = GRPC_CHTTP2_PARSE_OK == grpc_chttp2_window_update_parser_begin_frame(
-                                       &transport_parsing->simple.window_update,
-                                       transport_parsing->incoming_frame_size,
-                                       transport_parsing->incoming_frame_flags);
+  grpc_error *err = grpc_chttp2_window_update_parser_begin_frame(
+      &transport_parsing->simple.window_update,
+      transport_parsing->incoming_frame_size,
+      transport_parsing->incoming_frame_flags);
+  if (err != GRPC_ERROR_NONE) return err;
   if (transport_parsing->incoming_stream_id != 0) {
     grpc_chttp2_stream_parsing *stream_parsing =
         transport_parsing->incoming_stream = grpc_chttp2_parsing_lookup_stream(
@@ -826,26 +856,27 @@
   }
   transport_parsing->parser = grpc_chttp2_window_update_parser_parse;
   transport_parsing->parser_data = &transport_parsing->simple.window_update;
-  return ok;
+  return GRPC_ERROR_NONE;
 }
 
-static int init_ping_parser(grpc_exec_ctx *exec_ctx,
-                            grpc_chttp2_transport_parsing *transport_parsing) {
-  int ok = GRPC_CHTTP2_PARSE_OK == grpc_chttp2_ping_parser_begin_frame(
-                                       &transport_parsing->simple.ping,
-                                       transport_parsing->incoming_frame_size,
-                                       transport_parsing->incoming_frame_flags);
+static grpc_error *init_ping_parser(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing) {
+  grpc_error *err = grpc_chttp2_ping_parser_begin_frame(
+      &transport_parsing->simple.ping, transport_parsing->incoming_frame_size,
+      transport_parsing->incoming_frame_flags);
+  if (err != GRPC_ERROR_NONE) return err;
   transport_parsing->parser = grpc_chttp2_ping_parser_parse;
   transport_parsing->parser_data = &transport_parsing->simple.ping;
-  return ok;
+  return GRPC_ERROR_NONE;
 }
 
-static int init_rst_stream_parser(
+static grpc_error *init_rst_stream_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing) {
-  int ok = GRPC_CHTTP2_PARSE_OK == grpc_chttp2_rst_stream_parser_begin_frame(
-                                       &transport_parsing->simple.rst_stream,
-                                       transport_parsing->incoming_frame_size,
-                                       transport_parsing->incoming_frame_flags);
+  grpc_error *err = grpc_chttp2_rst_stream_parser_begin_frame(
+      &transport_parsing->simple.rst_stream,
+      transport_parsing->incoming_frame_size,
+      transport_parsing->incoming_frame_flags);
+  if (err != GRPC_ERROR_NONE) return err;
   grpc_chttp2_stream_parsing *stream_parsing =
       transport_parsing->incoming_stream = grpc_chttp2_parsing_lookup_stream(
           transport_parsing, transport_parsing->incoming_stream_id);
@@ -855,37 +886,32 @@
   stream_parsing->stats.incoming.framing_bytes += 9;
   transport_parsing->parser = grpc_chttp2_rst_stream_parser_parse;
   transport_parsing->parser_data = &transport_parsing->simple.rst_stream;
-  return ok;
+  return GRPC_ERROR_NONE;
 }
 
-static int init_goaway_parser(
+static grpc_error *init_goaway_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing) {
-  int ok = GRPC_CHTTP2_PARSE_OK == grpc_chttp2_goaway_parser_begin_frame(
-                                       &transport_parsing->goaway_parser,
-                                       transport_parsing->incoming_frame_size,
-                                       transport_parsing->incoming_frame_flags);
+  grpc_error *err = grpc_chttp2_goaway_parser_begin_frame(
+      &transport_parsing->goaway_parser, transport_parsing->incoming_frame_size,
+      transport_parsing->incoming_frame_flags);
+  if (err != GRPC_ERROR_NONE) return err;
   transport_parsing->parser = grpc_chttp2_goaway_parser_parse;
   transport_parsing->parser_data = &transport_parsing->goaway_parser;
-  return ok;
+  return GRPC_ERROR_NONE;
 }
 
-static int init_settings_frame_parser(
+static grpc_error *init_settings_frame_parser(
     grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing) {
-  int ok;
-
   if (transport_parsing->incoming_stream_id != 0) {
-    gpr_log(GPR_ERROR, "settings frame received for grpc_chttp2_stream %d",
-            transport_parsing->incoming_stream_id);
-    return 0;
+    return GRPC_ERROR_CREATE("Settings frame received for grpc_chttp2_stream");
   }
 
-  ok = GRPC_CHTTP2_PARSE_OK == grpc_chttp2_settings_parser_begin_frame(
-                                   &transport_parsing->simple.settings,
-                                   transport_parsing->incoming_frame_size,
-                                   transport_parsing->incoming_frame_flags,
-                                   transport_parsing->settings);
-  if (!ok) {
-    return 0;
+  grpc_error *err = grpc_chttp2_settings_parser_begin_frame(
+      &transport_parsing->simple.settings,
+      transport_parsing->incoming_frame_size,
+      transport_parsing->incoming_frame_flags, transport_parsing->settings);
+  if (err != GRPC_ERROR_NONE) {
+    return err;
   }
   if (transport_parsing->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) {
     transport_parsing->settings_ack_received = 1;
@@ -899,7 +925,7 @@
   }
   transport_parsing->parser = grpc_chttp2_settings_parser_parse;
   transport_parsing->parser_data = &transport_parsing->simple.settings;
-  return ok;
+  return GRPC_ERROR_NONE;
 }
 
 /*
@@ -908,34 +934,37 @@
 }
 */
 
-static int parse_frame_slice(grpc_exec_ctx *exec_ctx,
-                             grpc_chttp2_transport_parsing *transport_parsing,
-                             gpr_slice slice, int is_last) {
+static grpc_error *parse_frame_slice(
+    grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing,
+    gpr_slice slice, int is_last) {
   grpc_chttp2_stream_parsing *stream_parsing =
       transport_parsing->incoming_stream;
-  switch (transport_parsing->parser(exec_ctx, transport_parsing->parser_data,
-                                    transport_parsing, stream_parsing, slice,
-                                    is_last)) {
-    case GRPC_CHTTP2_PARSE_OK:
-      if (stream_parsing) {
-        grpc_chttp2_list_add_parsing_seen_stream(transport_parsing,
-                                                 stream_parsing);
-      }
-      return 1;
-    case GRPC_CHTTP2_STREAM_ERROR:
-      grpc_chttp2_parsing_become_skip_parser(exec_ctx, transport_parsing);
-      if (stream_parsing) {
-        stream_parsing->saw_rst_stream = 1;
-        stream_parsing->rst_stream_reason = GRPC_CHTTP2_PROTOCOL_ERROR;
-        gpr_slice_buffer_add(
-            &transport_parsing->qbuf,
-            grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id,
-                                          GRPC_CHTTP2_PROTOCOL_ERROR,
-                                          &stream_parsing->stats.outgoing));
-      }
-      return 1;
-    case GRPC_CHTTP2_CONNECTION_ERROR:
-      return 0;
+  grpc_error *err = transport_parsing->parser(
+      exec_ctx, transport_parsing->parser_data, transport_parsing,
+      stream_parsing, slice, is_last);
+  if (err == GRPC_ERROR_NONE) {
+    if (stream_parsing) {
+      grpc_chttp2_list_add_parsing_seen_stream(transport_parsing,
+                                               stream_parsing);
+    }
+    return GRPC_ERROR_NONE;
+  } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) {
+    if (grpc_http_trace) {
+      const char *msg = grpc_error_string(err);
+      gpr_log(GPR_ERROR, "%s", msg);
+      grpc_error_free_string(msg);
+    }
+    grpc_chttp2_parsing_become_skip_parser(exec_ctx, transport_parsing);
+    if (stream_parsing) {
+      stream_parsing->forced_close_error = err;
+      gpr_slice_buffer_add(
+          &transport_parsing->qbuf,
+          grpc_chttp2_rst_stream_create(transport_parsing->incoming_stream_id,
+                                        GRPC_CHTTP2_PROTOCOL_ERROR,
+                                        &stream_parsing->stats.outgoing));
+    } else {
+      GRPC_ERROR_UNREF(err);
+    }
   }
-  GPR_UNREACHABLE_CODE(return 0);
+  return err;
 }
diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c
index a8fb463..ca56deb 100644
--- a/src/core/ext/transport/chttp2/transport/writing.c
+++ b/src/core/ext/transport/chttp2/transport/writing.c
@@ -187,7 +187,8 @@
     grpc_endpoint_write(exec_ctx, endpoint, &transport_writing->outbuf,
                         &transport_writing->done_cb);
   } else {
-    grpc_exec_ctx_enqueue(exec_ctx, &transport_writing->done_cb, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, &transport_writing->done_cb, GRPC_ERROR_NONE,
+                       NULL);
   }
 }
 
@@ -334,25 +335,27 @@
       transport_global, transport_writing, &stream_global, &stream_writing)) {
     if (stream_writing->sent_initial_metadata) {
       grpc_chttp2_complete_closure_step(
-          exec_ctx, stream_global,
-          &stream_global->send_initial_metadata_finished, 1);
+          exec_ctx, transport_global, stream_global,
+          &stream_global->send_initial_metadata_finished, GRPC_ERROR_NONE);
     }
     grpc_transport_move_one_way_stats(&stream_writing->stats,
                                       &stream_global->stats.outgoing);
     if (stream_writing->sent_message) {
       GPR_ASSERT(stream_writing->send_message == NULL);
       grpc_chttp2_complete_closure_step(
-          exec_ctx, stream_global, &stream_global->send_message_finished, 1);
+          exec_ctx, transport_global, stream_global,
+          &stream_global->send_message_finished, GRPC_ERROR_NONE);
       stream_writing->sent_message = 0;
     }
     if (stream_writing->sent_trailing_metadata) {
       grpc_chttp2_complete_closure_step(
-          exec_ctx, stream_global,
-          &stream_global->send_trailing_metadata_finished, 1);
+          exec_ctx, transport_global, stream_global,
+          &stream_global->send_trailing_metadata_finished, GRPC_ERROR_NONE);
     }
     if (stream_writing->sent_trailing_metadata) {
       grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global,
-                                     !transport_global->is_client, 1);
+                                     !transport_global->is_client, 1,
+                                     GRPC_ERROR_NONE);
     }
     GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2_writing");
   }
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.c b/src/core/ext/transport/cronet/transport/cronet_transport.c
index 5bb0851..ea0f2bc 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.c
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.c
@@ -155,11 +155,11 @@
 static void enqueue_callbacks(grpc_closure *callback_list[]) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   if (callback_list[0]) {
-    grpc_exec_ctx_enqueue(&exec_ctx, callback_list[0], true, NULL);
+    grpc_exec_ctx_push(&exec_ctx, callback_list[0], GRPC_ERROR_NONE, NULL);
     callback_list[0] = NULL;
   }
   if (callback_list[1]) {
-    grpc_exec_ctx_enqueue(&exec_ctx, callback_list[1], true, NULL);
+    grpc_exec_ctx_push(&exec_ctx, callback_list[1], GRPC_ERROR_NONE, NULL);
     callback_list[1] = NULL;
   }
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/src/core/lib/channel/compress_filter.c b/src/core/lib/channel/compress_filter.c
index 5510c79..e029ec5 100644
--- a/src/core/lib/channel/compress_filter.c
+++ b/src/core/lib/channel/compress_filter.c
@@ -155,11 +155,11 @@
 static void continue_send_message(grpc_exec_ctx *exec_ctx,
                                   grpc_call_element *elem);
 
-static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, bool success) {
+static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
   grpc_call_element *elem = elemp;
   call_data *calld = elem->call_data;
   gpr_slice_buffer_reset_and_unref(&calld->slices);
-  calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, success);
+  calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, error);
 }
 
 static void finish_send_message(grpc_exec_ctx *exec_ctx,
@@ -205,7 +205,7 @@
   grpc_call_next_op(exec_ctx, elem, &calld->send_op);
 }
 
-static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, bool success) {
+static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, grpc_error *error) {
   grpc_call_element *elem = elemp;
   call_data *calld = elem->call_data;
   gpr_slice_buffer_add(&calld->slices, calld->incoming_slice);
diff --git a/src/core/lib/channel/http_client_filter.c b/src/core/lib/channel/http_client_filter.c
index 516e708..ced3d75 100644
--- a/src/core/lib/channel/http_client_filter.c
+++ b/src/core/lib/channel/http_client_filter.c
@@ -80,7 +80,8 @@
   return md;
 }
 
-static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
+static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
+                       grpc_error *error) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
   client_recv_filter_args a;
@@ -88,7 +89,7 @@
   a.exec_ctx = exec_ctx;
   grpc_metadata_batch_filter(calld->recv_initial_metadata, client_recv_filter,
                              &a);
-  calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
+  calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, error);
 }
 
 static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) {
diff --git a/src/core/lib/channel/http_server_filter.c b/src/core/lib/channel/http_server_filter.c
index ba86541..876eadd 100644
--- a/src/core/lib/channel/http_server_filter.c
+++ b/src/core/lib/channel/http_server_filter.c
@@ -142,10 +142,11 @@
   }
 }
 
-static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
+static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
+                       grpc_error *err) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
-  if (success) {
+  if (err == GRPC_ERROR_NONE) {
     server_filter_args a;
     a.elem = elem;
     a.exec_ctx = exec_ctx;
@@ -157,27 +158,35 @@
         calld->seen_path && calld->seen_authority) {
       /* do nothing */
     } else {
+      err = GRPC_ERROR_CREATE("Bad incoming HTTP headers");
       if (!calld->seen_path) {
-        gpr_log(GPR_ERROR, "Missing :path header");
+        err = grpc_error_add_child(err,
+                                   GRPC_ERROR_CREATE("Missing :path header"));
       }
       if (!calld->seen_authority) {
-        gpr_log(GPR_ERROR, "Missing :authority header");
+        err = grpc_error_add_child(
+            err, GRPC_ERROR_CREATE("Missing :authority header"));
       }
       if (!calld->seen_method) {
-        gpr_log(GPR_ERROR, "Missing :method header");
+        err = grpc_error_add_child(err,
+                                   GRPC_ERROR_CREATE("Missing :method header"));
       }
       if (!calld->seen_scheme) {
-        gpr_log(GPR_ERROR, "Missing :scheme header");
+        err = grpc_error_add_child(err,
+                                   GRPC_ERROR_CREATE("Missing :scheme header"));
       }
       if (!calld->seen_te_trailers) {
-        gpr_log(GPR_ERROR, "Missing te trailers header");
+        err = grpc_error_add_child(
+            err, GRPC_ERROR_CREATE("Missing te: trailers header"));
       }
       /* Error this call out */
-      success = 0;
       grpc_call_element_send_cancel(exec_ctx, elem);
     }
+  } else {
+    GRPC_ERROR_REF(err);
   }
-  calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
+  calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, err);
+  GRPC_ERROR_UNREF(err);
 }
 
 static void hs_mutate_op(grpc_call_element *elem,
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
index f22721a..4bda734 100644
--- a/src/core/lib/http/httpcli.c
+++ b/src/core/lib/http/httpcli.c
@@ -39,12 +39,14 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
 
 #include "src/core/lib/http/format_request.h"
 #include "src/core/lib/http/parser.h"
 #include "src/core/lib/iomgr/endpoint.h"
 #include "src/core/lib/iomgr/iomgr_internal.h"
 #include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/iomgr/tcp_client.h"
 #include "src/core/lib/support/string.h"
 
@@ -59,8 +61,7 @@
   gpr_timespec deadline;
   int have_read_byte;
   const grpc_httpcli_handshaker *handshaker;
-  grpc_httpcli_response_cb on_response;
-  void *user_data;
+  grpc_closure *on_done;
   grpc_httpcli_context *context;
   grpc_pollset *pollset;
   grpc_iomgr_object iomgr_obj;
@@ -69,6 +70,7 @@
   grpc_closure on_read;
   grpc_closure done_write;
   grpc_closure connected;
+  grpc_error *overall_error;
 } internal_request;
 
 static grpc_httpcli_get_override g_get_override = NULL;
@@ -93,14 +95,14 @@
   grpc_pollset_set_destroy(context->pollset_set);
 }
 
-static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req);
+static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req,
+                         grpc_error *due_to_error);
 
 static void finish(grpc_exec_ctx *exec_ctx, internal_request *req,
-                   int success) {
+                   grpc_error *error) {
   grpc_pollset_set_del_pollset(exec_ctx, req->context->pollset_set,
                                req->pollset);
-  req->on_response(exec_ctx, req->user_data,
-                   success ? &req->parser.http.response : NULL);
+  grpc_exec_ctx_push(exec_ctx, req->on_done, error, NULL);
   grpc_http_parser_destroy(&req->parser);
   if (req->addresses != NULL) {
     grpc_resolved_addresses_destroy(req->addresses);
@@ -114,39 +116,49 @@
   grpc_iomgr_unregister_object(&req->iomgr_obj);
   gpr_slice_buffer_destroy(&req->incoming);
   gpr_slice_buffer_destroy(&req->outgoing);
+  GRPC_ERROR_UNREF(req->overall_error);
   gpr_free(req);
 }
 
-static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success);
+static void append_error(internal_request *req, grpc_error *error) {
+  if (req->overall_error == GRPC_ERROR_NONE) {
+    req->overall_error = GRPC_ERROR_CREATE("Failed HTTP/1 client request");
+  }
+  grpc_resolved_address *addr = &req->addresses->addrs[req->next_address - 1];
+  char *addr_text = grpc_sockaddr_to_uri((struct sockaddr *)addr->addr);
+  req->overall_error = grpc_error_add_child(
+      req->overall_error,
+      grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, addr_text));
+  gpr_free(addr_text);
+}
 
 static void do_read(grpc_exec_ctx *exec_ctx, internal_request *req) {
   grpc_endpoint_read(exec_ctx, req->ep, &req->incoming, &req->on_read);
 }
 
-static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
+static void on_read(grpc_exec_ctx *exec_ctx, void *user_data,
+                    grpc_error *error) {
   internal_request *req = user_data;
   size_t i;
 
   for (i = 0; i < req->incoming.count; i++) {
     if (GPR_SLICE_LENGTH(req->incoming.slices[i])) {
       req->have_read_byte = 1;
-      if (!grpc_http_parser_parse(&req->parser, req->incoming.slices[i])) {
-        finish(exec_ctx, req, 0);
+      grpc_error *err =
+          grpc_http_parser_parse(&req->parser, req->incoming.slices[i]);
+      if (err != GRPC_ERROR_NONE) {
+        finish(exec_ctx, req, err);
         return;
       }
     }
   }
 
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     do_read(exec_ctx, req);
   } else if (!req->have_read_byte) {
-    next_address(exec_ctx, req);
+    next_address(exec_ctx, req, GRPC_ERROR_REF(error));
   } else {
-    int parse_success = grpc_http_parser_eof(&req->parser);
-    if (parse_success && (req->parser.type != GRPC_HTTP_RESPONSE)) {
-      parse_success = 0;
-    }
-    finish(exec_ctx, req, parse_success);
+    finish(exec_ctx, req, grpc_http_parser_eof(&req->parser));
   }
 }
 
@@ -154,12 +166,12 @@
   do_read(exec_ctx, req);
 }
 
-static void done_write(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void done_write(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   internal_request *req = arg;
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     on_written(exec_ctx, req);
   } else {
-    next_address(exec_ctx, req);
+    next_address(exec_ctx, req, GRPC_ERROR_REF(error));
   }
 }
 
@@ -174,7 +186,8 @@
   internal_request *req = arg;
 
   if (!ep) {
-    next_address(exec_ctx, req);
+    next_address(exec_ctx, req,
+                 GRPC_ERROR_CREATE("Unexplained handshake failure"));
     return;
   }
 
@@ -182,11 +195,12 @@
   start_write(exec_ctx, req);
 }
 
-static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void on_connected(grpc_exec_ctx *exec_ctx, void *arg,
+                         grpc_error *error) {
   internal_request *req = arg;
 
   if (!req->ep) {
-    next_address(exec_ctx, req);
+    next_address(exec_ctx, req, GRPC_ERROR_REF(error));
     return;
   }
   req->handshaker->handshake(
@@ -195,10 +209,16 @@
       on_handshake_done);
 }
 
-static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) {
+static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req,
+                         grpc_error *error) {
   grpc_resolved_address *addr;
+  if (error != GRPC_ERROR_NONE) {
+    append_error(req, error);
+  }
   if (req->next_address == req->addresses->naddrs) {
-    finish(exec_ctx, req, 0);
+    finish(exec_ctx, req,
+           GRPC_ERROR_CREATE_REFERENCING("Failed HTTP requests to all targets",
+                                         &req->overall_error, 1));
     return;
   }
   addr = &req->addresses->addrs[req->next_address++];
@@ -208,34 +228,34 @@
       (struct sockaddr *)&addr->addr, addr->len, req->deadline);
 }
 
-static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
-                        grpc_resolved_addresses *addresses) {
+static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   internal_request *req = arg;
-  if (!addresses) {
-    finish(exec_ctx, req, 0);
+  if (error != GRPC_ERROR_NONE) {
+    finish(exec_ctx, req, error);
     return;
   }
-  req->addresses = addresses;
   req->next_address = 0;
-  next_address(exec_ctx, req);
+  next_address(exec_ctx, req, GRPC_ERROR_NONE);
 }
 
-static void internal_request_begin(
-    grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
-    grpc_pollset *pollset, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data, const char *name, gpr_slice request_text) {
+static void internal_request_begin(grpc_exec_ctx *exec_ctx,
+                                   grpc_httpcli_context *context,
+                                   grpc_pollset *pollset,
+                                   const grpc_httpcli_request *request,
+                                   gpr_timespec deadline, grpc_closure *on_done,
+                                   grpc_httpcli_response *response,
+                                   const char *name, gpr_slice request_text) {
   internal_request *req = gpr_malloc(sizeof(internal_request));
   memset(req, 0, sizeof(*req));
   req->request_text = request_text;
-  grpc_http_parser_init(&req->parser);
-  req->on_response = on_response;
-  req->user_data = user_data;
+  grpc_http_parser_init(&req->parser, GRPC_HTTP_RESPONSE, response);
+  req->on_done = on_done;
   req->deadline = deadline;
   req->handshaker =
       request->handshaker ? request->handshaker : &grpc_httpcli_plaintext;
   req->context = context;
   req->pollset = pollset;
+  req->overall_error = GRPC_ERROR_NONE;
   grpc_closure_init(&req->on_read, on_read, req);
   grpc_closure_init(&req->done_write, done_write, req);
   gpr_slice_buffer_init(&req->incoming);
@@ -247,22 +267,22 @@
   grpc_pollset_set_add_pollset(exec_ctx, req->context->pollset_set,
                                req->pollset);
   grpc_resolve_address(exec_ctx, request->host, req->handshaker->default_port,
-                       on_resolved, req);
+                       grpc_closure_create(on_resolved, req), &req->addresses);
 }
 
 void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
                       grpc_pollset *pollset,
                       const grpc_httpcli_request *request,
-                      gpr_timespec deadline,
-                      grpc_httpcli_response_cb on_response, void *user_data) {
+                      gpr_timespec deadline, grpc_closure *on_done,
+                      grpc_httpcli_response *response) {
   char *name;
   if (g_get_override &&
-      g_get_override(exec_ctx, request, deadline, on_response, user_data)) {
+      g_get_override(exec_ctx, request, deadline, on_done, response)) {
     return;
   }
   gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path);
-  internal_request_begin(exec_ctx, context, pollset, request, deadline,
-                         on_response, user_data, name,
+  internal_request_begin(exec_ctx, context, pollset, request, deadline, on_done,
+                         response, name,
                          grpc_httpcli_format_get_request(request));
   gpr_free(name);
 }
@@ -271,18 +291,18 @@
                        grpc_pollset *pollset,
                        const grpc_httpcli_request *request,
                        const char *body_bytes, size_t body_size,
-                       gpr_timespec deadline,
-                       grpc_httpcli_response_cb on_response, void *user_data) {
+                       gpr_timespec deadline, grpc_closure *on_done,
+                       grpc_httpcli_response *response) {
   char *name;
   if (g_post_override &&
       g_post_override(exec_ctx, request, body_bytes, body_size, deadline,
-                      on_response, user_data)) {
+                      on_done, response)) {
     return;
   }
   gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path);
   internal_request_begin(
-      exec_ctx, context, pollset, request, deadline, on_response, user_data,
-      name, grpc_httpcli_format_post_request(request, body_bytes, body_size));
+      exec_ctx, context, pollset, request, deadline, on_done, response, name,
+      grpc_httpcli_format_post_request(request, body_bytes, body_size));
   gpr_free(name);
 }
 
diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h
index 11a32a1..2d57864 100644
--- a/src/core/lib/http/httpcli.h
+++ b/src/core/lib/http/httpcli.h
@@ -81,11 +81,6 @@
 /* Expose the parser response type as a httpcli response too */
 typedef struct grpc_http_response grpc_httpcli_response;
 
-/* Callback for grpc_httpcli_get and grpc_httpcli_post. */
-typedef void (*grpc_httpcli_response_cb)(grpc_exec_ctx *exec_ctx,
-                                         void *user_data,
-                                         const grpc_http_response *response);
-
 void grpc_httpcli_context_init(grpc_httpcli_context *context);
 void grpc_httpcli_context_destroy(grpc_httpcli_context *context);
 
@@ -102,8 +97,8 @@
 void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
                       grpc_pollset *pollset,
                       const grpc_httpcli_request *request,
-                      gpr_timespec deadline,
-                      grpc_httpcli_response_cb on_response, void *user_data);
+                      gpr_timespec deadline, grpc_closure *on_complete,
+                      grpc_httpcli_response *response);
 
 /* Asynchronously perform a HTTP POST.
    'context' specifies the http context under which to do the post
@@ -124,19 +119,19 @@
                        grpc_pollset *pollset,
                        const grpc_httpcli_request *request,
                        const char *body_bytes, size_t body_size,
-                       gpr_timespec deadline,
-                       grpc_httpcli_response_cb on_response, void *user_data);
+                       gpr_timespec deadline, grpc_closure *on_complete,
+                       grpc_httpcli_response *response);
 
 /* override functions return 1 if they handled the request, 0 otherwise */
 typedef int (*grpc_httpcli_get_override)(grpc_exec_ctx *exec_ctx,
                                          const grpc_httpcli_request *request,
                                          gpr_timespec deadline,
-                                         grpc_httpcli_response_cb on_response,
-                                         void *user_data);
+                                         grpc_closure *on_complete,
+                                         grpc_httpcli_response *response);
 typedef int (*grpc_httpcli_post_override)(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
     const char *body_bytes, size_t body_size, gpr_timespec deadline,
-    grpc_httpcli_response_cb on_response, void *user_data);
+    grpc_closure *on_complete, grpc_httpcli_response *response);
 
 void grpc_httpcli_set_override(grpc_httpcli_get_override get,
                                grpc_httpcli_post_override post);
diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c
index ea4bff3..5590928 100644
--- a/src/core/lib/http/httpcli_security_connector.c
+++ b/src/core/lib/http/httpcli_security_connector.c
@@ -38,7 +38,7 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
-#include "src/core/lib/security/handshake.h"
+#include "src/core/lib/security/transport/handshake.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/tsi/ssl_transport_security.h"
 
diff --git a/src/core/lib/http/parser.c b/src/core/lib/http/parser.c
index 09b2ed4..92ed08a 100644
--- a/src/core/lib/http/parser.c
+++ b/src/core/lib/http/parser.c
@@ -48,37 +48,38 @@
   return out;
 }
 
-static int handle_response_line(grpc_http_parser *parser) {
+static grpc_error *handle_response_line(grpc_http_parser *parser) {
   uint8_t *beg = parser->cur_line;
   uint8_t *cur = beg;
   uint8_t *end = beg + parser->cur_line_length;
 
-  if (cur == end || *cur++ != 'H') goto error;
-  if (cur == end || *cur++ != 'T') goto error;
-  if (cur == end || *cur++ != 'T') goto error;
-  if (cur == end || *cur++ != 'P') goto error;
-  if (cur == end || *cur++ != '/') goto error;
-  if (cur == end || *cur++ != '1') goto error;
-  if (cur == end || *cur++ != '.') goto error;
-  if (cur == end || *cur < '0' || *cur++ > '1') goto error;
-  if (cur == end || *cur++ != ' ') goto error;
-  if (cur == end || *cur < '1' || *cur++ > '9') goto error;
-  if (cur == end || *cur < '0' || *cur++ > '9') goto error;
-  if (cur == end || *cur < '0' || *cur++ > '9') goto error;
-  parser->http.response.status =
+  if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'");
+  if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'");
+  if (cur == end || *cur++ != '1') return GRPC_ERROR_CREATE("Expected '1'");
+  if (cur == end || *cur++ != '.') return GRPC_ERROR_CREATE("Expected '.'");
+  if (cur == end || *cur < '0' || *cur++ > '1') {
+    return GRPC_ERROR_CREATE("Expected HTTP/1.0 or HTTP/1.1");
+  }
+  if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '");
+  if (cur == end || *cur < '1' || *cur++ > '9')
+    return GRPC_ERROR_CREATE("Expected status code");
+  if (cur == end || *cur < '0' || *cur++ > '9')
+    return GRPC_ERROR_CREATE("Expected status code");
+  if (cur == end || *cur < '0' || *cur++ > '9')
+    return GRPC_ERROR_CREATE("Expected status code");
+  parser->http.response->status =
       (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0');
-  if (cur == end || *cur++ != ' ') goto error;
+  if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '");
 
   /* we don't really care about the status code message */
 
-  return 1;
-
-error:
-  if (grpc_http1_trace) gpr_log(GPR_ERROR, "Failed parsing response line");
-  return 0;
+  return GRPC_ERROR_NONE;
 }
 
-static int handle_request_line(grpc_http_parser *parser) {
+static grpc_error *handle_request_line(grpc_http_parser *parser) {
   uint8_t *beg = parser->cur_line;
   uint8_t *cur = beg;
   uint8_t *end = beg + parser->cur_line_length;
@@ -87,84 +88,81 @@
 
   while (cur != end && *cur++ != ' ')
     ;
-  if (cur == end) goto error;
-  parser->http.request.method = buf2str(beg, (size_t)(cur - beg - 1));
+  if (cur == end) return GRPC_ERROR_CREATE("No method on HTTP request line");
+  parser->http.request->method = buf2str(beg, (size_t)(cur - beg - 1));
 
   beg = cur;
   while (cur != end && *cur++ != ' ')
     ;
-  if (cur == end) goto error;
-  parser->http.request.path = buf2str(beg, (size_t)(cur - beg - 1));
+  if (cur == end) return GRPC_ERROR_CREATE("No path on HTTP request line");
+  parser->http.request->path = buf2str(beg, (size_t)(cur - beg - 1));
 
-  if (cur == end || *cur++ != 'H') goto error;
-  if (cur == end || *cur++ != 'T') goto error;
-  if (cur == end || *cur++ != 'T') goto error;
-  if (cur == end || *cur++ != 'P') goto error;
-  if (cur == end || *cur++ != '/') goto error;
+  if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'");
+  if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'");
   vers_major = (uint8_t)(*cur++ - '1' + 1);
   ++cur;
-  if (cur == end) goto error;
+  if (cur == end)
+    return GRPC_ERROR_CREATE("End of line in HTTP version string");
   vers_minor = (uint8_t)(*cur++ - '1' + 1);
 
   if (vers_major == 1) {
     if (vers_minor == 0) {
-      parser->http.request.version = GRPC_HTTP_HTTP10;
+      parser->http.request->version = GRPC_HTTP_HTTP10;
     } else if (vers_minor == 1) {
-      parser->http.request.version = GRPC_HTTP_HTTP11;
+      parser->http.request->version = GRPC_HTTP_HTTP11;
     } else {
-      goto error;
+      return GRPC_ERROR_CREATE(
+          "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
     }
   } else if (vers_major == 2) {
     if (vers_minor == 0) {
-      parser->http.request.version = GRPC_HTTP_HTTP20;
+      parser->http.request->version = GRPC_HTTP_HTTP20;
     } else {
-      goto error;
+      return GRPC_ERROR_CREATE(
+          "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
     }
   } else {
-    goto error;
+    return GRPC_ERROR_CREATE("Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
   }
 
-  return 1;
-
-error:
-  if (grpc_http1_trace) gpr_log(GPR_ERROR, "Failed parsing request line");
-  return 0;
+  return GRPC_ERROR_NONE;
 }
 
-static int handle_first_line(grpc_http_parser *parser) {
-  if (parser->cur_line[0] == 'H') {
-    parser->type = GRPC_HTTP_RESPONSE;
-    return handle_response_line(parser);
-  } else {
-    parser->type = GRPC_HTTP_REQUEST;
-    return handle_request_line(parser);
+static grpc_error *handle_first_line(grpc_http_parser *parser) {
+  switch (parser->type) {
+    case GRPC_HTTP_REQUEST:
+      return handle_request_line(parser);
+    case GRPC_HTTP_RESPONSE:
+      return handle_response_line(parser);
   }
+  GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
 }
 
-static int add_header(grpc_http_parser *parser) {
+static grpc_error *add_header(grpc_http_parser *parser) {
   uint8_t *beg = parser->cur_line;
   uint8_t *cur = beg;
   uint8_t *end = beg + parser->cur_line_length;
   size_t *hdr_count = NULL;
   grpc_http_header **hdrs = NULL;
   grpc_http_header hdr = {NULL, NULL};
+  grpc_error *error = GRPC_ERROR_NONE;
 
   GPR_ASSERT(cur != end);
 
   if (*cur == ' ' || *cur == '\t') {
-    if (grpc_http1_trace)
-      gpr_log(GPR_ERROR, "Continued header lines not supported yet");
-    goto error;
+    error = GRPC_ERROR_CREATE("Continued header lines not supported yet");
+    goto done;
   }
 
   while (cur != end && *cur != ':') {
     cur++;
   }
   if (cur == end) {
-    if (grpc_http1_trace) {
-      gpr_log(GPR_ERROR, "Didn't find ':' in header string");
-    }
-    goto error;
+    error = GRPC_ERROR_CREATE("Didn't find ':' in header string");
+    goto done;
   }
   GPR_ASSERT(cur >= beg);
   hdr.key = buf2str(beg, (size_t)(cur - beg));
@@ -176,14 +174,15 @@
   GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length);
   hdr.value = buf2str(cur, (size_t)(end - cur) - parser->cur_line_end_length);
 
-  if (parser->type == GRPC_HTTP_RESPONSE) {
-    hdr_count = &parser->http.response.hdr_count;
-    hdrs = &parser->http.response.hdrs;
-  } else if (parser->type == GRPC_HTTP_REQUEST) {
-    hdr_count = &parser->http.request.hdr_count;
-    hdrs = &parser->http.request.hdrs;
-  } else {
-    return 0;
+  switch (parser->type) {
+    case GRPC_HTTP_RESPONSE:
+      hdr_count = &parser->http.response->hdr_count;
+      hdrs = &parser->http.response->hdrs;
+      break;
+    case GRPC_HTTP_REQUEST:
+      hdr_count = &parser->http.request->hdr_count;
+      hdrs = &parser->http.request->hdrs;
+      break;
   }
 
   if (*hdr_count == parser->hdr_capacity) {
@@ -192,20 +191,21 @@
     *hdrs = gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs));
   }
   (*hdrs)[(*hdr_count)++] = hdr;
-  return 1;
 
-error:
-  gpr_free(hdr.key);
-  gpr_free(hdr.value);
-  return 0;
+done:
+  if (error != GRPC_ERROR_NONE) {
+    gpr_free(hdr.key);
+    gpr_free(hdr.value);
+  }
+  return error;
 }
 
-static int finish_line(grpc_http_parser *parser) {
+static grpc_error *finish_line(grpc_http_parser *parser) {
+  grpc_error *err;
   switch (parser->state) {
     case GRPC_HTTP_FIRST_LINE:
-      if (!handle_first_line(parser)) {
-        return 0;
-      }
+      err = handle_first_line(parser);
+      if (err != GRPC_ERROR_NONE) return err;
       parser->state = GRPC_HTTP_HEADERS;
       break;
     case GRPC_HTTP_HEADERS:
@@ -213,30 +213,31 @@
         parser->state = GRPC_HTTP_BODY;
         break;
       }
-      if (!add_header(parser)) {
-        return 0;
+      err = add_header(parser);
+      if (err != GRPC_ERROR_NONE) {
+        return err;
       }
       break;
     case GRPC_HTTP_BODY:
-      GPR_UNREACHABLE_CODE(return 0);
+      GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
   }
 
   parser->cur_line_length = 0;
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
-static int addbyte_body(grpc_http_parser *parser, uint8_t byte) {
+static grpc_error *addbyte_body(grpc_http_parser *parser, uint8_t byte) {
   size_t *body_length = NULL;
   char **body = NULL;
 
   if (parser->type == GRPC_HTTP_RESPONSE) {
-    body_length = &parser->http.response.body_length;
-    body = &parser->http.response.body;
+    body_length = &parser->http.response->body_length;
+    body = &parser->http.response->body;
   } else if (parser->type == GRPC_HTTP_REQUEST) {
-    body_length = &parser->http.request.body_length;
-    body = &parser->http.request.body;
+    body_length = &parser->http.request->body_length;
+    body = &parser->http.request->body;
   } else {
-    return 0;
+    GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
   }
 
   if (*body_length == parser->body_capacity) {
@@ -246,34 +247,34 @@
   (*body)[*body_length] = (char)byte;
   (*body_length)++;
 
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
-static int check_line(grpc_http_parser *parser) {
+static bool check_line(grpc_http_parser *parser) {
   if (parser->cur_line_length >= 2 &&
       parser->cur_line[parser->cur_line_length - 2] == '\r' &&
       parser->cur_line[parser->cur_line_length - 1] == '\n') {
-    return 1;
+    return true;
   }
 
   // HTTP request with \n\r line termiantors.
   else if (parser->cur_line_length >= 2 &&
            parser->cur_line[parser->cur_line_length - 2] == '\n' &&
            parser->cur_line[parser->cur_line_length - 1] == '\r') {
-    return 1;
+    return true;
   }
 
   // HTTP request with only \n line terminators.
   else if (parser->cur_line_length >= 1 &&
            parser->cur_line[parser->cur_line_length - 1] == '\n') {
     parser->cur_line_end_length = 1;
-    return 1;
+    return true;
   }
 
-  return 0;
+  return false;
 }
 
-static int addbyte(grpc_http_parser *parser, uint8_t byte) {
+static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte) {
   switch (parser->state) {
     case GRPC_HTTP_FIRST_LINE:
     case GRPC_HTTP_HEADERS:
@@ -288,7 +289,7 @@
       if (check_line(parser)) {
         return finish_line(parser);
       } else {
-        return 1;
+        return GRPC_ERROR_NONE;
       }
       GPR_UNREACHABLE_CODE(return 0);
     case GRPC_HTTP_BODY:
@@ -297,46 +298,53 @@
   GPR_UNREACHABLE_CODE(return 0);
 }
 
-void grpc_http_parser_init(grpc_http_parser *parser) {
+void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type,
+                           void *request_or_response) {
   memset(parser, 0, sizeof(*parser));
   parser->state = GRPC_HTTP_FIRST_LINE;
-  parser->type = GRPC_HTTP_UNKNOWN;
+  parser->type = type;
+  parser->http.request_or_response = request_or_response;
   parser->cur_line_end_length = 2;
 }
 
-void grpc_http_parser_destroy(grpc_http_parser *parser) {
+void grpc_http_parser_destroy(grpc_http_parser *parser) {}
+
+void grpc_http_request_destroy(grpc_http_request *request) {
   size_t i;
-  if (parser->type == GRPC_HTTP_RESPONSE) {
-    gpr_free(parser->http.response.body);
-    for (i = 0; i < parser->http.response.hdr_count; i++) {
-      gpr_free(parser->http.response.hdrs[i].key);
-      gpr_free(parser->http.response.hdrs[i].value);
-    }
-    gpr_free(parser->http.response.hdrs);
-  } else if (parser->type == GRPC_HTTP_REQUEST) {
-    gpr_free(parser->http.request.body);
-    for (i = 0; i < parser->http.request.hdr_count; i++) {
-      gpr_free(parser->http.request.hdrs[i].key);
-      gpr_free(parser->http.request.hdrs[i].value);
-    }
-    gpr_free(parser->http.request.hdrs);
-    gpr_free(parser->http.request.method);
-    gpr_free(parser->http.request.path);
+  gpr_free(request->body);
+  for (i = 0; i < request->hdr_count; i++) {
+    gpr_free(request->hdrs[i].key);
+    gpr_free(request->hdrs[i].value);
   }
+  gpr_free(request->hdrs);
+  gpr_free(request->method);
+  gpr_free(request->path);
 }
 
-int grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) {
+void grpc_http_response_destroy(grpc_http_response *response) {
+  size_t i;
+  gpr_free(response->body);
+  for (i = 0; i < response->hdr_count; i++) {
+    gpr_free(response->hdrs[i].key);
+    gpr_free(response->hdrs[i].value);
+  }
+  gpr_free(response->hdrs);
+}
+
+grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) {
   size_t i;
 
   for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) {
-    if (!addbyte(parser, GPR_SLICE_START_PTR(slice)[i])) {
-      return 0;
-    }
+    grpc_error *err = addbyte(parser, GPR_SLICE_START_PTR(slice)[i]);
+    if (err != GRPC_ERROR_NONE) return err;
   }
 
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
-int grpc_http_parser_eof(grpc_http_parser *parser) {
-  return parser->state == GRPC_HTTP_BODY;
+grpc_error *grpc_http_parser_eof(grpc_http_parser *parser) {
+  if (parser->state != GRPC_HTTP_BODY) {
+    return GRPC_ERROR_CREATE("Did not finish headers");
+  }
+  return GRPC_ERROR_NONE;
 }
diff --git a/src/core/lib/http/parser.c.orig b/src/core/lib/http/parser.c.orig
new file mode 100644
index 0000000..74d90fd
--- /dev/null
+++ b/src/core/lib/http/parser.c.orig
@@ -0,0 +1,357 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/http/parser.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+int grpc_http1_trace = 0;
+
+static char *buf2str(void *buffer, size_t length) {
+  char *out = gpr_malloc(length + 1);
+  memcpy(out, buffer, length);
+  out[length] = 0;
+  return out;
+}
+
+static grpc_error *handle_response_line(grpc_http_parser *parser) {
+  uint8_t *beg = parser->cur_line;
+  uint8_t *cur = beg;
+  uint8_t *end = beg + parser->cur_line_length;
+
+  if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'");
+  if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'");
+  if (cur == end || *cur++ != '1') return GRPC_ERROR_CREATE("Expected '1'");
+  if (cur == end || *cur++ != '.') return GRPC_ERROR_CREATE("Expected '.'");
+  if (cur == end || *cur < '0' || *cur++ > '1') {
+    return GRPC_ERROR_CREATE("Expected HTTP/1.0 or HTTP/1.1");
+  }
+  if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '");
+  if (cur == end || *cur < '1' || *cur++ > '9')
+    return GRPC_ERROR_CREATE("Expected status code");
+  if (cur == end || *cur < '0' || *cur++ > '9')
+    return GRPC_ERROR_CREATE("Expected status code");
+  if (cur == end || *cur < '0' || *cur++ > '9')
+    return GRPC_ERROR_CREATE("Expected status code");
+  parser->http.response->status =
+      (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0');
+  if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '");
+
+  /* we don't really care about the status code message */
+
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error *handle_request_line(grpc_http_parser *parser) {
+  uint8_t *beg = parser->cur_line;
+  uint8_t *cur = beg;
+  uint8_t *end = beg + parser->cur_line_length;
+  uint8_t vers_major = 0;
+  uint8_t vers_minor = 0;
+
+  while (cur != end && *cur++ != ' ')
+    ;
+  if (cur == end) return GRPC_ERROR_CREATE("No method on HTTP request line");
+  parser->http.request->method = buf2str(beg, (size_t)(cur - beg - 1));
+
+  beg = cur;
+  while (cur != end && *cur++ != ' ')
+    ;
+  if (cur == end) return GRPC_ERROR_CREATE("No path on HTTP request line");
+  parser->http.request->path = buf2str(beg, (size_t)(cur - beg - 1));
+
+  if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'");
+  if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'");
+  if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'");
+  vers_major = (uint8_t)(*cur++ - '1' + 1);
+  ++cur;
+  if (cur == end)
+    return GRPC_ERROR_CREATE("End of line in HTTP version string");
+  vers_minor = (uint8_t)(*cur++ - '1' + 1);
+
+  if (vers_major == 1) {
+    if (vers_minor == 0) {
+      parser->http.request->version = GRPC_HTTP_HTTP10;
+    } else if (vers_minor == 1) {
+      parser->http.request->version = GRPC_HTTP_HTTP11;
+    } else {
+      return GRPC_ERROR_CREATE(
+          "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
+    }
+  } else if (vers_major == 2) {
+    if (vers_minor == 0) {
+      parser->http.request->version = GRPC_HTTP_HTTP20;
+    } else {
+      return GRPC_ERROR_CREATE(
+          "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
+    }
+  } else {
+    return GRPC_ERROR_CREATE("Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0");
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error *handle_first_line(grpc_http_parser *parser) {
+  switch (parser->type) {
+    case GRPC_HTTP_REQUEST:
+      return handle_request_line(parser);
+    case GRPC_HTTP_RESPONSE:
+      return handle_response_line(parser);
+  }
+  GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
+}
+
+static grpc_error *add_header(grpc_http_parser *parser) {
+  uint8_t *beg = parser->cur_line;
+  uint8_t *cur = beg;
+  uint8_t *end = beg + parser->cur_line_length;
+  size_t *hdr_count = NULL;
+  grpc_http_header **hdrs = NULL;
+  grpc_http_header hdr = {NULL, NULL};
+  grpc_error *error = GRPC_ERROR_NONE;
+
+  GPR_ASSERT(cur != end);
+
+  if (*cur == ' ' || *cur == '\t') {
+    error = GRPC_ERROR_CREATE("Continued header lines not supported yet");
+    goto done;
+  }
+
+  while (cur != end && *cur != ':') {
+    cur++;
+  }
+  if (cur == end) {
+<<<<<<< HEAD
+    error = GRPC_ERROR_CREATE("Didn't find ':' in header string");
+    goto done;
+=======
+    if (grpc_http1_trace) {
+      gpr_log(GPR_ERROR, "Didn't find ':' in header string");
+    }
+    goto error;
+>>>>>>> a709afe241d8b264a1c326315f757b4a8d330207
+  }
+  GPR_ASSERT(cur >= beg);
+  hdr.key = buf2str(beg, (size_t)(cur - beg));
+  cur++; /* skip : */
+
+  while (cur != end && (*cur == ' ' || *cur == '\t')) {
+    cur++;
+  }
+  GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length);
+  hdr.value = buf2str(cur, (size_t)(end - cur) - parser->cur_line_end_length);
+
+  switch (parser->type) {
+    case GRPC_HTTP_RESPONSE:
+      hdr_count = &parser->http.response->hdr_count;
+      hdrs = &parser->http.response->hdrs;
+      break;
+    case GRPC_HTTP_REQUEST:
+      hdr_count = &parser->http.request->hdr_count;
+      hdrs = &parser->http.request->hdrs;
+      break;
+  }
+
+  if (*hdr_count == parser->hdr_capacity) {
+    parser->hdr_capacity =
+        GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2);
+    *hdrs = gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs));
+  }
+  (*hdrs)[(*hdr_count)++] = hdr;
+
+done:
+  if (error != GRPC_ERROR_NONE) {
+    gpr_free(hdr.key);
+    gpr_free(hdr.value);
+  }
+  return error;
+}
+
+static grpc_error *finish_line(grpc_http_parser *parser) {
+  grpc_error *err;
+  switch (parser->state) {
+    case GRPC_HTTP_FIRST_LINE:
+      err = handle_first_line(parser);
+      if (err != GRPC_ERROR_NONE) return err;
+      parser->state = GRPC_HTTP_HEADERS;
+      break;
+    case GRPC_HTTP_HEADERS:
+      if (parser->cur_line_length == parser->cur_line_end_length) {
+        parser->state = GRPC_HTTP_BODY;
+        break;
+      }
+      err = add_header(parser);
+      if (err != GRPC_ERROR_NONE) {
+        return err;
+      }
+      break;
+    case GRPC_HTTP_BODY:
+      GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
+  }
+
+  parser->cur_line_length = 0;
+  return GRPC_ERROR_NONE;
+}
+
+static grpc_error *addbyte_body(grpc_http_parser *parser, uint8_t byte) {
+  size_t *body_length = NULL;
+  char **body = NULL;
+
+  if (parser->type == GRPC_HTTP_RESPONSE) {
+    body_length = &parser->http.response->body_length;
+    body = &parser->http.response->body;
+  } else if (parser->type == GRPC_HTTP_REQUEST) {
+    body_length = &parser->http.request->body_length;
+    body = &parser->http.request->body;
+  } else {
+    GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here"));
+  }
+
+  if (*body_length == parser->body_capacity) {
+    parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2);
+    *body = gpr_realloc((void *)*body, parser->body_capacity);
+  }
+  (*body)[*body_length] = (char)byte;
+  (*body_length)++;
+
+  return GRPC_ERROR_NONE;
+}
+
+static bool check_line(grpc_http_parser *parser) {
+  if (parser->cur_line_length >= 2 &&
+      parser->cur_line[parser->cur_line_length - 2] == '\r' &&
+      parser->cur_line[parser->cur_line_length - 1] == '\n') {
+    return true;
+  }
+
+  // HTTP request with \n\r line termiantors.
+  else if (parser->cur_line_length >= 2 &&
+           parser->cur_line[parser->cur_line_length - 2] == '\n' &&
+           parser->cur_line[parser->cur_line_length - 1] == '\r') {
+    return true;
+  }
+
+  // HTTP request with only \n line terminators.
+  else if (parser->cur_line_length >= 1 &&
+           parser->cur_line[parser->cur_line_length - 1] == '\n') {
+    parser->cur_line_end_length = 1;
+    return true;
+  }
+
+  return false;
+}
+
+static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte) {
+  switch (parser->state) {
+    case GRPC_HTTP_FIRST_LINE:
+    case GRPC_HTTP_HEADERS:
+      if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) {
+        if (grpc_http1_trace)
+          gpr_log(GPR_ERROR, "HTTP client max line length (%d) exceeded",
+                  GRPC_HTTP_PARSER_MAX_HEADER_LENGTH);
+        return 0;
+      }
+      parser->cur_line[parser->cur_line_length] = byte;
+      parser->cur_line_length++;
+      if (check_line(parser)) {
+        return finish_line(parser);
+      } else {
+        return GRPC_ERROR_NONE;
+      }
+      GPR_UNREACHABLE_CODE(return 0);
+    case GRPC_HTTP_BODY:
+      return addbyte_body(parser, byte);
+  }
+  GPR_UNREACHABLE_CODE(return 0);
+}
+
+void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type,
+                           void *request_or_response) {
+  memset(parser, 0, sizeof(*parser));
+  parser->state = GRPC_HTTP_FIRST_LINE;
+  parser->type = type;
+  parser->http.request_or_response = request_or_response;
+  parser->cur_line_end_length = 2;
+}
+
+void grpc_http_parser_destroy(grpc_http_parser *parser) {}
+
+void grpc_http_request_destroy(grpc_http_request *request) {
+  size_t i;
+  gpr_free(request->body);
+  for (i = 0; i < request->hdr_count; i++) {
+    gpr_free(request->hdrs[i].key);
+    gpr_free(request->hdrs[i].value);
+  }
+  gpr_free(request->hdrs);
+  gpr_free(request->method);
+  gpr_free(request->path);
+}
+
+void grpc_http_response_destroy(grpc_http_response *response) {
+  size_t i;
+  gpr_free(response->body);
+  for (i = 0; i < response->hdr_count; i++) {
+    gpr_free(response->hdrs[i].key);
+    gpr_free(response->hdrs[i].value);
+  }
+  gpr_free(response->hdrs);
+}
+
+grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) {
+  size_t i;
+
+  for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) {
+    grpc_error *err = addbyte(parser, GPR_SLICE_START_PTR(slice)[i]);
+    if (err != GRPC_ERROR_NONE) return err;
+  }
+
+  return GRPC_ERROR_NONE;
+}
+
+grpc_error *grpc_http_parser_eof(grpc_http_parser *parser) {
+  if (parser->state != GRPC_HTTP_BODY) {
+    return GRPC_ERROR_CREATE("Did not finish headers");
+  }
+  return GRPC_ERROR_NONE;
+}
diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h
index 536637e..6df3cc8 100644
--- a/src/core/lib/http/parser.h
+++ b/src/core/lib/http/parser.h
@@ -36,6 +36,7 @@
 
 #include <grpc/support/port_platform.h>
 #include <grpc/support/slice.h>
+#include "src/core/lib/iomgr/error.h"
 
 /* Maximum length of a header string of the form 'Key: Value\r\n' */
 #define GRPC_HTTP_PARSER_MAX_HEADER_LENGTH 4096
@@ -61,7 +62,6 @@
 typedef enum {
   GRPC_HTTP_RESPONSE,
   GRPC_HTTP_REQUEST,
-  GRPC_HTTP_UNKNOWN
 } grpc_http_type;
 
 /* A request */
@@ -97,8 +97,9 @@
   grpc_http_type type;
 
   union {
-    grpc_http_response response;
-    grpc_http_request request;
+    grpc_http_response *response;
+    grpc_http_request *request;
+    void *request_or_response;
   } http;
   size_t body_capacity;
   size_t hdr_capacity;
@@ -108,11 +109,15 @@
   size_t cur_line_end_length;
 } grpc_http_parser;
 
-void grpc_http_parser_init(grpc_http_parser *parser);
+void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type,
+                           void *request_or_response);
 void grpc_http_parser_destroy(grpc_http_parser *parser);
 
-int grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice);
-int grpc_http_parser_eof(grpc_http_parser *parser);
+grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice);
+grpc_error *grpc_http_parser_eof(grpc_http_parser *parser);
+
+void grpc_http_request_destroy(grpc_http_request *request);
+void grpc_http_response_destroy(grpc_http_response *response);
 
 extern int grpc_http1_trace;
 
diff --git a/src/core/lib/iomgr/closure.c b/src/core/lib/iomgr/closure.c
index 27793c3..0b6c3b2 100644
--- a/src/core/lib/iomgr/closure.c
+++ b/src/core/lib/iomgr/closure.c
@@ -39,25 +39,32 @@
                        void *cb_arg) {
   closure->cb = cb;
   closure->cb_arg = cb_arg;
-  closure->final_data = 0;
 }
 
-void grpc_closure_list_add(grpc_closure_list *closure_list,
-                           grpc_closure *closure, bool success) {
-  if (closure == NULL) return;
-  closure->final_data = (success != 0);
+void grpc_closure_list_append(grpc_closure_list *closure_list,
+                              grpc_closure *closure, grpc_error *error) {
+  if (closure == NULL) {
+    GRPC_ERROR_UNREF(error);
+    return;
+  }
+  closure->error = error;
+  closure->next_data.next = NULL;
   if (closure_list->head == NULL) {
     closure_list->head = closure;
   } else {
-    closure_list->tail->final_data |= (uintptr_t)closure;
+    closure_list->tail->next_data.next = closure;
   }
   closure_list->tail = closure;
 }
 
-void grpc_closure_list_fail_all(grpc_closure_list *list) {
-  for (grpc_closure *c = list->head; c != NULL; c = grpc_closure_next(c)) {
-    c->final_data &= ~(uintptr_t)1;
+void grpc_closure_list_fail_all(grpc_closure_list *list,
+                                grpc_error *forced_failure) {
+  for (grpc_closure *c = list->head; c != NULL; c = c->next_data.next) {
+    if (c->error == GRPC_ERROR_NONE) {
+      c->error = GRPC_ERROR_REF(forced_failure);
+    }
   }
+  GRPC_ERROR_UNREF(forced_failure);
 }
 
 bool grpc_closure_list_empty(grpc_closure_list closure_list) {
@@ -71,7 +78,7 @@
   if (dst->head == NULL) {
     *dst = *src;
   } else {
-    dst->tail->final_data |= (uintptr_t)src->head;
+    dst->tail->next_data.next = src->head;
     dst->tail = src->tail;
   }
   src->head = src->tail = NULL;
@@ -83,12 +90,13 @@
   grpc_closure wrapper;
 } wrapped_closure;
 
-static void closure_wrapper(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void closure_wrapper(grpc_exec_ctx *exec_ctx, void *arg,
+                            grpc_error *error) {
   wrapped_closure *wc = arg;
   grpc_iomgr_cb_func cb = wc->cb;
   void *cb_arg = wc->cb_arg;
   gpr_free(wc);
-  cb(exec_ctx, cb_arg, success);
+  cb(exec_ctx, cb_arg, error);
 }
 
 grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg) {
@@ -98,7 +106,3 @@
   grpc_closure_init(&wc->wrapper, closure_wrapper, wc);
   return &wc->wrapper;
 }
-
-grpc_closure *grpc_closure_next(grpc_closure *closure) {
-  return (grpc_closure *)(closure->final_data & ~(uintptr_t)1);
-}
diff --git a/src/core/lib/iomgr/closure.h b/src/core/lib/iomgr/closure.h
index fdc2dae..344b7d3 100644
--- a/src/core/lib/iomgr/closure.h
+++ b/src/core/lib/iomgr/closure.h
@@ -36,6 +36,7 @@
 
 #include <grpc/support/port_platform.h>
 #include <stdbool.h>
+#include "src/core/lib/iomgr/error.h"
 
 struct grpc_closure;
 typedef struct grpc_closure grpc_closure;
@@ -55,7 +56,7 @@
  * \param success An indication on the state of the iomgr. On false, cleanup
  * actions should be taken (eg, shutdown). */
 typedef void (*grpc_iomgr_cb_func)(grpc_exec_ctx *exec_ctx, void *arg,
-                                   bool success);
+                                   grpc_error *error);
 
 /** A closure over a grpc_iomgr_cb_func. */
 struct grpc_closure {
@@ -65,10 +66,12 @@
   /** Arguments to be passed to "cb". */
   void *cb_arg;
 
-  /** Once enqueued, contains in the lower bit the success of the closure,
-      and in the upper bits the pointer to the next closure in the list.
-      Before enqueing for execution, this is usable for scratch data. */
-  uintptr_t final_data;
+  grpc_error *error;
+
+  union {
+    grpc_closure *next;
+    uintptr_t scratch;
+  } next_data;
 };
 
 /** Initializes \a closure with \a cb and \a cb_arg. */
@@ -83,11 +86,12 @@
 
 /** add \a closure to the end of \a list and set \a closure's success to \a
  * success */
-void grpc_closure_list_add(grpc_closure_list *list, grpc_closure *closure,
-                           bool success);
+void grpc_closure_list_append(grpc_closure_list *list, grpc_closure *closure,
+                              grpc_error *error);
 
 /** force all success bits in \a list to false */
-void grpc_closure_list_fail_all(grpc_closure_list *list);
+void grpc_closure_list_fail_all(grpc_closure_list *list,
+                                grpc_error *forced_failure);
 
 /** append all closures from \a src to \a dst and empty \a src. */
 void grpc_closure_list_move(grpc_closure_list *src, grpc_closure_list *dst);
@@ -95,7 +99,4 @@
 /** return whether \a list is empty. */
 bool grpc_closure_list_empty(grpc_closure_list list);
 
-/** return the next pointer for a queued closure list */
-grpc_closure *grpc_closure_next(grpc_closure *closure);
-
 #endif /* GRPC_CORE_LIB_IOMGR_CLOSURE_H */
diff --git a/src/core/lib/iomgr/endpoint_pair_posix.c b/src/core/lib/iomgr/endpoint_pair_posix.c
index e0ce47c..e295fb4 100644
--- a/src/core/lib/iomgr/endpoint_pair_posix.c
+++ b/src/core/lib/iomgr/endpoint_pair_posix.c
@@ -58,8 +58,8 @@
   GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0);
   flags = fcntl(sv[1], F_GETFL, 0);
   GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0);
-  GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]));
-  GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]));
+  GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]) == GRPC_ERROR_NONE);
+  GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]) == GRPC_ERROR_NONE);
 }
 
 grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name,
diff --git a/src/core/lib/iomgr/error.c b/src/core/lib/iomgr/error.c
new file mode 100644
index 0000000..c70eb1a
--- /dev/null
+++ b/src/core/lib/iomgr/error.c
@@ -0,0 +1,534 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/iomgr/error.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/avl.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+
+static void destroy_integer(void *key) {}
+
+static void *copy_integer(void *key) { return key; }
+
+static long compare_integers(void *key1, void *key2) {
+  return GPR_ICMP((uintptr_t)key1, (uintptr_t)key2);
+}
+
+static void destroy_string(void *str) { gpr_free(str); }
+
+static void *copy_string(void *str) { return gpr_strdup(str); }
+
+static void destroy_err(void *err) { GRPC_ERROR_UNREF(err); }
+
+static void *copy_err(void *err) { return GRPC_ERROR_REF(err); }
+
+static void destroy_time(void *tm) { gpr_free(tm); }
+
+static gpr_timespec *box_time(gpr_timespec tm) {
+  gpr_timespec *out = gpr_malloc(sizeof(*out));
+  *out = tm;
+  return out;
+}
+
+static void *copy_time(void *tm) { return box_time(*(gpr_timespec *)tm); }
+
+static const gpr_avl_vtable avl_vtable_ints = {destroy_integer, copy_integer,
+                                               compare_integers,
+                                               destroy_integer, copy_integer};
+
+static const gpr_avl_vtable avl_vtable_strs = {destroy_integer, copy_integer,
+                                               compare_integers, destroy_string,
+                                               copy_string};
+
+static const gpr_avl_vtable avl_vtable_times = {
+    destroy_integer, copy_integer, compare_integers, destroy_time, copy_time};
+
+static const gpr_avl_vtable avl_vtable_errs = {
+    destroy_integer, copy_integer, compare_integers, destroy_err, copy_err};
+
+static const char *error_int_name(grpc_error_ints key) {
+  switch (key) {
+    case GRPC_ERROR_INT_STATUS_CODE:
+      return "status_code";
+    case GRPC_ERROR_INT_ERRNO:
+      return "errno";
+    case GRPC_ERROR_INT_FILE_LINE:
+      return "file_line";
+    case GRPC_ERROR_INT_WARNING:
+      return "warning";
+    case GRPC_ERROR_INT_STREAM_ID:
+      return "stream_id";
+    case GRPC_ERROR_INT_GRPC_STATUS:
+      return "grpc_status";
+    case GRPC_ERROR_INT_OFFSET:
+      return "offset";
+    case GRPC_ERROR_INT_INDEX:
+      return "index";
+    case GRPC_ERROR_INT_SIZE:
+      return "size";
+    case GRPC_ERROR_INT_HTTP2_ERROR:
+      return "http2_error";
+    case GRPC_ERROR_INT_TSI_CODE:
+      return "tsi_code";
+    case GRPC_ERROR_INT_SECURITY_STATUS:
+      return "security_status";
+    case GRPC_ERROR_INT_FD:
+      return "fd";
+    case GRPC_ERROR_INT_WSA_ERROR:
+      return "wsa_error";
+  }
+  GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+static const char *error_str_name(grpc_error_strs key) {
+  switch (key) {
+    case GRPC_ERROR_STR_DESCRIPTION:
+      return "description";
+    case GRPC_ERROR_STR_OS_ERROR:
+      return "os_error";
+    case GRPC_ERROR_STR_TARGET_ADDRESS:
+      return "target_address";
+    case GRPC_ERROR_STR_SYSCALL:
+      return "syscall";
+    case GRPC_ERROR_STR_FILE:
+      return "file";
+    case GRPC_ERROR_STR_GRPC_MESSAGE:
+      return "grpc_message";
+    case GRPC_ERROR_STR_RAW_BYTES:
+      return "raw_bytes";
+    case GRPC_ERROR_STR_TSI_ERROR:
+      return "tsi_error";
+    case GRPC_ERROR_STR_FILENAME:
+      return "filename";
+  }
+  GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+static const char *error_time_name(grpc_error_times key) {
+  switch (key) {
+    case GRPC_ERROR_TIME_CREATED:
+      return "created";
+  }
+  GPR_UNREACHABLE_CODE(return "unknown");
+}
+
+struct grpc_error {
+  gpr_refcount refs;
+  gpr_avl ints;
+  gpr_avl strs;
+  gpr_avl times;
+  gpr_avl errs;
+  uintptr_t next_err;
+};
+
+static bool is_special(grpc_error *err) {
+  return err == GRPC_ERROR_NONE || err == GRPC_ERROR_OOM ||
+         err == GRPC_ERROR_CANCELLED;
+}
+
+#ifdef GRPC_ERROR_REFCOUNT_DEBUG
+grpc_error *grpc_error_ref(grpc_error *err, const char *file, int line,
+                           const char *func) {
+  if (is_special(err)) return err;
+  gpr_log(GPR_DEBUG, "%p: %d -> %d [%s:%d %s]", err, err->refs.count,
+          err->refs.count + 1, file, line, func);
+  gpr_ref(&err->refs);
+  return err;
+}
+#else
+grpc_error *grpc_error_ref(grpc_error *err) {
+  if (is_special(err)) return err;
+  gpr_ref(&err->refs);
+  return err;
+}
+#endif
+
+static void error_destroy(grpc_error *err) {
+  GPR_ASSERT(!is_special(err));
+  gpr_avl_unref(err->ints);
+  gpr_avl_unref(err->strs);
+  gpr_avl_unref(err->errs);
+  gpr_avl_unref(err->times);
+  gpr_free(err);
+}
+
+#ifdef GRPC_ERROR_REFCOUNT_DEBUG
+void grpc_error_unref(grpc_error *err, const char *file, int line,
+                      const char *func) {
+  if (is_special(err)) return;
+  gpr_log(GPR_DEBUG, "%p: %d -> %d [%s:%d %s]", err, err->refs.count,
+          err->refs.count - 1, file, line, func);
+  if (gpr_unref(&err->refs)) {
+    error_destroy(err);
+  }
+}
+#else
+void grpc_error_unref(grpc_error *err) {
+  if (is_special(err)) return;
+  if (gpr_unref(&err->refs)) {
+    error_destroy(err);
+  }
+}
+#endif
+
+grpc_error *grpc_error_create(const char *file, int line, const char *desc,
+                              grpc_error **referencing,
+                              size_t num_referencing) {
+  grpc_error *err = gpr_malloc(sizeof(*err));
+  if (err == NULL) {  // TODO(ctiller): make gpr_malloc return NULL
+    return GRPC_ERROR_OOM;
+  }
+#ifdef GRPC_ERROR_REFCOUNT_DEBUG
+  gpr_log(GPR_DEBUG, "%p create [%s:%d]", err, file, line);
+#endif
+  err->ints = gpr_avl_add(gpr_avl_create(&avl_vtable_ints),
+                          (void *)(uintptr_t)GRPC_ERROR_INT_FILE_LINE,
+                          (void *)(uintptr_t)line);
+  err->strs = gpr_avl_add(
+      gpr_avl_add(gpr_avl_create(&avl_vtable_strs),
+                  (void *)(uintptr_t)GRPC_ERROR_STR_FILE, gpr_strdup(file)),
+      (void *)(uintptr_t)GRPC_ERROR_STR_DESCRIPTION, gpr_strdup(desc));
+  err->errs = gpr_avl_create(&avl_vtable_errs);
+  for (size_t i = 0; i < num_referencing; i++) {
+    if (referencing[i] == GRPC_ERROR_NONE) continue;
+    err->errs = gpr_avl_add(err->errs, (void *)(err->next_err++),
+                            GRPC_ERROR_REF(referencing[i]));
+  }
+  err->times = gpr_avl_add(gpr_avl_create(&avl_vtable_times),
+                           (void *)(uintptr_t)GRPC_ERROR_TIME_CREATED,
+                           box_time(gpr_now(GPR_CLOCK_REALTIME)));
+  err->next_err = 0;
+  gpr_ref_init(&err->refs, 1);
+  return err;
+}
+
+static grpc_error *copy_error_and_unref(grpc_error *in) {
+  if (is_special(in)) {
+    if (in == GRPC_ERROR_NONE) return GRPC_ERROR_CREATE("no error");
+    if (in == GRPC_ERROR_OOM) return GRPC_ERROR_CREATE("oom");
+    if (in == GRPC_ERROR_CANCELLED) return GRPC_ERROR_CREATE("cancelled");
+    return GRPC_ERROR_CREATE("unknown");
+  }
+  grpc_error *out = gpr_malloc(sizeof(*out));
+#ifdef GRPC_ERROR_REFCOUNT_DEBUG
+  gpr_log(GPR_DEBUG, "%p create copying", out);
+#endif
+  out->ints = gpr_avl_ref(in->ints);
+  out->strs = gpr_avl_ref(in->strs);
+  out->errs = gpr_avl_ref(in->errs);
+  out->times = gpr_avl_ref(in->times);
+  out->next_err = in->next_err;
+  gpr_ref_init(&out->refs, 1);
+  GRPC_ERROR_UNREF(in);
+  return out;
+}
+
+grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which,
+                               intptr_t value) {
+  grpc_error *new = copy_error_and_unref(src);
+  new->ints = gpr_avl_add(new->ints, (void *)(uintptr_t)which, (void *)value);
+  return new;
+}
+
+bool grpc_error_get_int(grpc_error *err, grpc_error_ints which, intptr_t *p) {
+  void *pp;
+  if (gpr_avl_maybe_get(err->ints, (void *)(uintptr_t)which, &pp)) {
+    if (p != NULL) *p = (intptr_t)pp;
+    return true;
+  }
+  return false;
+}
+
+grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which,
+                               const char *value) {
+  grpc_error *new = copy_error_and_unref(src);
+  new->strs =
+      gpr_avl_add(new->strs, (void *)(uintptr_t)which, gpr_strdup(value));
+  return new;
+}
+
+grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child) {
+  grpc_error *new = copy_error_and_unref(src);
+  new->errs = gpr_avl_add(new->errs, (void *)(new->next_err++), child);
+  return new;
+}
+
+static const char *no_error_string = "null";
+static const char *oom_error_string = "\"Out of memory\"";
+static const char *cancelled_error_string = "\"Cancelled\"";
+
+typedef struct {
+  char *key;
+  char *value;
+} kv_pair;
+
+typedef struct {
+  kv_pair *kvs;
+  size_t num_kvs;
+  size_t cap_kvs;
+} kv_pairs;
+
+static void append_kv(kv_pairs *kvs, char *key, char *value) {
+  if (kvs->num_kvs == kvs->cap_kvs) {
+    kvs->cap_kvs = GPR_MAX(3 * kvs->cap_kvs / 2, 4);
+    kvs->kvs = gpr_realloc(kvs->kvs, sizeof(*kvs->kvs) * kvs->cap_kvs);
+  }
+  kvs->kvs[kvs->num_kvs].key = key;
+  kvs->kvs[kvs->num_kvs].value = value;
+  kvs->num_kvs++;
+}
+
+static void collect_kvs(gpr_avl_node *node, char *key(void *k),
+                        char *fmt(void *v), kv_pairs *kvs) {
+  if (node == NULL) return;
+  append_kv(kvs, key(node->key), fmt(node->value));
+  collect_kvs(node->left, key, fmt, kvs);
+  collect_kvs(node->right, key, fmt, kvs);
+}
+
+static char *key_int(void *p) {
+  return gpr_strdup(error_int_name((grpc_error_ints)(uintptr_t)p));
+}
+
+static char *key_str(void *p) {
+  return gpr_strdup(error_str_name((grpc_error_strs)(uintptr_t)p));
+}
+
+static char *key_time(void *p) {
+  return gpr_strdup(error_time_name((grpc_error_times)(uintptr_t)p));
+}
+
+static char *fmt_int(void *p) {
+  char *s;
+  gpr_asprintf(&s, "%lld", (intptr_t)p);
+  return s;
+}
+
+static void append_chr(char c, char **s, size_t *sz, size_t *cap) {
+  if (*sz == *cap) {
+    *cap = GPR_MAX(8, 3 * *cap / 2);
+    *s = gpr_realloc(*s, *cap);
+  }
+  (*s)[(*sz)++] = c;
+}
+
+static void append_str(const char *str, char **s, size_t *sz, size_t *cap) {
+  for (const char *c = str; *c; c++) {
+    append_chr(*c, s, sz, cap);
+  }
+}
+
+static void append_esc_str(const char *str, char **s, size_t *sz, size_t *cap) {
+  static const char *hex = "0123456789abcdef";
+  append_chr('"', s, sz, cap);
+  for (const uint8_t *c = (const uint8_t *)str; *c; c++) {
+    if (*c < 32 || *c >= 127) {
+      append_chr('\\', s, sz, cap);
+      switch (*c) {
+        case '\b':
+          append_chr('b', s, sz, cap);
+          break;
+        case '\f':
+          append_chr('f', s, sz, cap);
+          break;
+        case '\n':
+          append_chr('n', s, sz, cap);
+          break;
+        case '\r':
+          append_chr('r', s, sz, cap);
+          break;
+        case '\t':
+          append_chr('t', s, sz, cap);
+          break;
+        default:
+          append_chr('u', s, sz, cap);
+          append_chr('0', s, sz, cap);
+          append_chr('0', s, sz, cap);
+          append_chr(hex[*c >> 4], s, sz, cap);
+          append_chr(hex[*c & 0x0f], s, sz, cap);
+          break;
+      }
+    } else {
+      append_chr((char)*c, s, sz, cap);
+    }
+  }
+  append_chr('"', s, sz, cap);
+}
+
+static char *fmt_str(void *p) {
+  char *s = NULL;
+  size_t sz = 0;
+  size_t cap = 0;
+  append_esc_str(p, &s, &sz, &cap);
+  append_chr(0, &s, &sz, &cap);
+  return s;
+}
+
+static char *fmt_time(void *p) {
+  gpr_timespec tm = *(gpr_timespec *)p;
+  char *out;
+  char *pfx = "!!";
+  switch (tm.clock_type) {
+    case GPR_CLOCK_MONOTONIC:
+      pfx = "@monotonic:";
+      break;
+    case GPR_CLOCK_REALTIME:
+      pfx = "@";
+      break;
+    case GPR_CLOCK_PRECISE:
+      pfx = "@precise:";
+      break;
+    case GPR_TIMESPAN:
+      pfx = "";
+      break;
+  }
+  gpr_asprintf(&out, "\"%s%d.%09d\"", pfx, tm.tv_sec, tm.tv_nsec);
+  return out;
+}
+
+static void add_errs(gpr_avl_node *n, char **s, size_t *sz, size_t *cap) {
+  if (n == NULL) return;
+  add_errs(n->left, s, sz, cap);
+  const char *e = grpc_error_string(n->value);
+  append_str(e, s, sz, cap);
+  grpc_error_free_string(e);
+  add_errs(n->right, s, sz, cap);
+}
+
+static char *errs_string(grpc_error *err) {
+  char *s = NULL;
+  size_t sz = 0;
+  size_t cap = 0;
+  append_chr('[', &s, &sz, &cap);
+  add_errs(err->errs.root, &s, &sz, &cap);
+  append_chr(']', &s, &sz, &cap);
+  append_chr(0, &s, &sz, &cap);
+  return s;
+}
+
+static int cmp_kvs(const void *a, const void *b) {
+  const kv_pair *ka = a;
+  const kv_pair *kb = b;
+  return strcmp(ka->key, kb->key);
+}
+
+static const char *finish_kvs(kv_pairs *kvs) {
+  char *s = NULL;
+  size_t sz = 0;
+  size_t cap = 0;
+
+  append_chr('{', &s, &sz, &cap);
+  for (size_t i = 0; i < kvs->num_kvs; i++) {
+    if (i != 0) append_chr(',', &s, &sz, &cap);
+    append_esc_str(kvs->kvs[i].key, &s, &sz, &cap);
+    gpr_free(kvs->kvs[i].key);
+    append_chr(':', &s, &sz, &cap);
+    append_str(kvs->kvs[i].value, &s, &sz, &cap);
+    gpr_free(kvs->kvs[i].value);
+  }
+  append_chr('}', &s, &sz, &cap);
+  append_chr(0, &s, &sz, &cap);
+
+  gpr_free(kvs->kvs);
+  return s;
+}
+
+void grpc_error_free_string(const char *str) {
+  if (str == no_error_string) return;
+  if (str == oom_error_string) return;
+  if (str == cancelled_error_string) return;
+  gpr_free((char *)str);
+}
+
+const char *grpc_error_string(grpc_error *err) {
+  if (err == GRPC_ERROR_NONE) return no_error_string;
+  if (err == GRPC_ERROR_OOM) return oom_error_string;
+  if (err == GRPC_ERROR_CANCELLED) return cancelled_error_string;
+
+  kv_pairs kvs;
+  memset(&kvs, 0, sizeof(kvs));
+
+  collect_kvs(err->ints.root, key_int, fmt_int, &kvs);
+  collect_kvs(err->strs.root, key_str, fmt_str, &kvs);
+  collect_kvs(err->times.root, key_time, fmt_time, &kvs);
+  if (!gpr_avl_is_empty(err->errs)) {
+    append_kv(&kvs, gpr_strdup("referenced_errors"), errs_string(err));
+  }
+
+  qsort(kvs.kvs, kvs.num_kvs, sizeof(kv_pair), cmp_kvs);
+
+  return finish_kvs(&kvs);
+}
+
+#ifdef GPR_POSIX_SOCKET
+grpc_error *grpc_os_error(const char *file, int line, int err,
+                          const char *call_name) {
+  return grpc_error_set_str(
+      grpc_error_set_str(
+          grpc_error_set_int(grpc_error_create(file, line, "OS Error", NULL, 0),
+                             GRPC_ERROR_INT_ERRNO, err),
+          GRPC_ERROR_STR_OS_ERROR, strerror(err)),
+      GRPC_ERROR_STR_SYSCALL, call_name);
+}
+#endif
+
+#ifdef GPR_WIN32
+grpc_error *grpc_wsa_error(const char *file, int line, int err,
+                           const char *call_name) {
+  char *utf8_message = gpr_format_message(err);
+  grpc_error *error = grpc_error_set_str(
+      grpc_error_set_str(
+          grpc_error_set_int(grpc_error_create(file, line, "OS Error", NULL, 0),
+                             GRPC_ERROR_INT_WSA_ERROR, err),
+          GRPC_ERROR_STR_OS_ERROR, utf8_message),
+      GRPC_ERROR_STR_SYSCALL, call_name);
+  gpr_free(utf8_message);
+  return error;
+}
+#endif
+
+bool grpc_log_if_error(const char *what, grpc_error *error, const char *file,
+                       int line) {
+  if (error == GRPC_ERROR_NONE) return true;
+  const char *msg = grpc_error_string(error);
+  gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, "%s: %s", what, msg);
+  grpc_error_free_string(msg);
+  GRPC_ERROR_UNREF(error);
+  return false;
+}
diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h
new file mode 100644
index 0000000..695724c
--- /dev/null
+++ b/src/core/lib/iomgr/error.h
@@ -0,0 +1,143 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_ERROR_H
+#define GRPC_CORE_LIB_IOMGR_ERROR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <grpc/support/time.h>
+
+// Opaque representation of an error.
+// Errors are refcounted objects that represent the result of an operation.
+// Ownership laws:
+//  if a grpc_error is returned by a function, the caller owns a ref to that
+//    instance
+//  if a grpc_error is passed to a grpc_closure callback function (functions
+//    with the signature:
+//      void (*f)(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error))
+//    then those functions do not automatically own a ref to error
+//  if a grpc_error is passed to *ANY OTHER FUNCTION* then that function takes
+//    ownership of the error
+typedef struct grpc_error grpc_error;
+
+typedef enum {
+  GRPC_ERROR_INT_ERRNO,
+  GRPC_ERROR_INT_FILE_LINE,
+  GRPC_ERROR_INT_STATUS_CODE,
+  GRPC_ERROR_INT_WARNING,
+  GRPC_ERROR_INT_STREAM_ID,
+  GRPC_ERROR_INT_GRPC_STATUS,
+  GRPC_ERROR_INT_OFFSET,
+  GRPC_ERROR_INT_INDEX,
+  GRPC_ERROR_INT_SIZE,
+  GRPC_ERROR_INT_HTTP2_ERROR,
+  GRPC_ERROR_INT_TSI_CODE,
+  GRPC_ERROR_INT_SECURITY_STATUS,
+  GRPC_ERROR_INT_WSA_ERROR,
+  GRPC_ERROR_INT_FD,
+} grpc_error_ints;
+
+typedef enum {
+  GRPC_ERROR_STR_DESCRIPTION,
+  GRPC_ERROR_STR_FILE,
+  GRPC_ERROR_STR_OS_ERROR,
+  GRPC_ERROR_STR_SYSCALL,
+  GRPC_ERROR_STR_TARGET_ADDRESS,
+  GRPC_ERROR_STR_GRPC_MESSAGE,
+  GRPC_ERROR_STR_RAW_BYTES,
+  GRPC_ERROR_STR_TSI_ERROR,
+  GRPC_ERROR_STR_FILENAME,
+} grpc_error_strs;
+
+typedef enum {
+  GRPC_ERROR_TIME_CREATED,
+} grpc_error_times;
+
+#define GRPC_ERROR_NONE ((grpc_error *)NULL)
+#define GRPC_ERROR_OOM ((grpc_error *)1)
+#define GRPC_ERROR_CANCELLED ((grpc_error *)2)
+
+const char *grpc_error_string(grpc_error *error);
+void grpc_error_free_string(const char *str);
+
+grpc_error *grpc_error_create(const char *file, int line, const char *desc,
+                              grpc_error **referencing, size_t num_referencing);
+#define GRPC_ERROR_CREATE(desc) \
+  grpc_error_create(__FILE__, __LINE__, desc, NULL, 0)
+
+// Create an error that references some other errors. This function adds a
+// reference to each error in errs - it does not consume an existing reference
+#define GRPC_ERROR_CREATE_REFERENCING(desc, errs, count) \
+  grpc_error_create(__FILE__, __LINE__, desc, errs, count)
+
+//#define GRPC_ERROR_REFCOUNT_DEBUG
+#ifdef GRPC_ERROR_REFCOUNT_DEBUG
+grpc_error *grpc_error_ref(grpc_error *err, const char *file, int line,
+                           const char *func);
+void grpc_error_unref(grpc_error *err, const char *file, int line,
+                      const char *func);
+#define GRPC_ERROR_REF(err) grpc_error_ref(err, __FILE__, __LINE__, __func__)
+#define GRPC_ERROR_UNREF(err) \
+  grpc_error_unref(err, __FILE__, __LINE__, __func__)
+#else
+grpc_error *grpc_error_ref(grpc_error *err);
+void grpc_error_unref(grpc_error *err);
+#define GRPC_ERROR_REF(err) grpc_error_ref(err)
+#define GRPC_ERROR_UNREF(err) grpc_error_unref(err)
+#endif
+
+grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which,
+                               intptr_t value);
+bool grpc_error_get_int(grpc_error *error, grpc_error_ints which, intptr_t *p);
+grpc_error *grpc_error_set_time(grpc_error *src, grpc_error_times which,
+                                gpr_timespec value);
+grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which,
+                               const char *value);
+grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child);
+grpc_error *grpc_os_error(const char *file, int line, int err,
+                          const char *call_name);
+#define GRPC_OS_ERROR(err, call_name) \
+  grpc_os_error(__FILE__, __LINE__, err, call_name)
+grpc_error *grpc_wsa_error(const char *file, int line, int err,
+                           const char *call_name);
+#define GRPC_WSA_ERROR(err, call_name) \
+  grpc_wsa_error(__FILE__, __LINE__, err, call_name)
+
+bool grpc_log_if_error(const char *what, grpc_error *error, const char *file,
+                       int line);
+#define GRPC_LOG_IF_ERROR(what, error) \
+  grpc_log_if_error((what), (error), __FILE__, __LINE__)
+
+#endif /* GRPC_CORE_LIB_IOMGR_ERROR_H */
diff --git a/src/core/lib/iomgr/ev_poll_and_epoll_posix.c b/src/core/lib/iomgr/ev_poll_and_epoll_posix.c
index aeb6e28..7892ad6 100644
--- a/src/core/lib/iomgr/ev_poll_and_epoll_posix.c
+++ b/src/core/lib/iomgr/ev_poll_and_epoll_posix.c
@@ -51,6 +51,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <poll.h>
+#include <stdbool.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <unistd.h>
@@ -88,9 +89,9 @@
   gpr_atm refst;
 
   gpr_mu mu;
-  int shutdown;
-  int closed;
-  int released;
+  bool shutdown;
+  bool closed;
+  bool released;
 
   /* The watcher list.
 
@@ -186,8 +187,8 @@
 
 struct grpc_pollset_worker {
   grpc_cached_wakeup_fd *wakeup_fd;
-  int reevaluate_polling_on_wakeup;
-  int kicked_specifically;
+  bool reevaluate_polling_on_wakeup;
+  bool kicked_specifically;
   struct grpc_pollset_worker *next;
   struct grpc_pollset_worker *prev;
 };
@@ -201,9 +202,9 @@
   gpr_mu mu;
   grpc_pollset_worker root_worker;
   int in_flight_cbs;
-  int shutting_down;
-  int called_shutdown;
-  int kicked_without_pollers;
+  bool shutting_down;
+  bool called_shutdown;
+  bool kicked_without_pollers;
   grpc_closure *shutdown_done;
   grpc_closure_list idle_jobs;
   union {
@@ -217,9 +218,10 @@
 struct grpc_pollset_vtable {
   void (*add_fd)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                  struct grpc_fd *fd, int and_unlock_pollset);
-  void (*maybe_work_and_unlock)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                                grpc_pollset_worker *worker,
-                                gpr_timespec deadline, gpr_timespec now);
+  grpc_error *(*maybe_work_and_unlock)(grpc_exec_ctx *exec_ctx,
+                                       grpc_pollset *pollset,
+                                       grpc_pollset_worker *worker,
+                                       gpr_timespec deadline, gpr_timespec now);
   void (*finish_shutdown)(grpc_pollset *pollset);
   void (*destroy)(grpc_pollset *pollset);
 };
@@ -247,9 +249,9 @@
 #define GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP 2
 /* As per pollset_kick, with an extended set of flags (defined above)
    -- mostly for fd_posix's use. */
-static void pollset_kick_ext(grpc_pollset *p,
-                             grpc_pollset_worker *specific_worker,
-                             uint32_t flags);
+static grpc_error *pollset_kick_ext(grpc_pollset *p,
+                                    grpc_pollset_worker *specific_worker,
+                                    uint32_t flags) GRPC_MUST_USE_RESULT;
 
 /* turn a pollset into a multipoller: platform specific */
 typedef void (*platform_become_multipoller_type)(grpc_exec_ctx *exec_ctx,
@@ -331,7 +333,7 @@
 
   gpr_mu_lock(&r->mu);
   gpr_atm_rel_store(&r->refst, 1);
-  r->shutdown = 0;
+  r->shutdown = false;
   r->read_closure = CLOSURE_NOT_READY;
   r->write_closure = CLOSURE_NOT_READY;
   r->fd = fd;
@@ -340,8 +342,8 @@
   r->freelist_next = NULL;
   r->read_watcher = r->write_watcher = NULL;
   r->on_done_closure = NULL;
-  r->closed = 0;
-  r->released = 0;
+  r->closed = false;
+  r->released = false;
   gpr_mu_unlock(&r->mu);
   return r;
 }
@@ -415,12 +417,13 @@
   return (gpr_atm_acq_load(&fd->refst) & 1) == 0;
 }
 
-static void pollset_kick_locked(grpc_fd_watcher *watcher) {
+static grpc_error *pollset_kick_locked(grpc_fd_watcher *watcher) {
   gpr_mu_lock(&watcher->pollset->mu);
   GPR_ASSERT(watcher->worker);
-  pollset_kick_ext(watcher->pollset, watcher->worker,
-                   GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP);
+  grpc_error *err = pollset_kick_ext(watcher->pollset, watcher->worker,
+                                     GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP);
   gpr_mu_unlock(&watcher->pollset->mu);
+  return err;
 }
 
 static void maybe_wake_one_watcher_locked(grpc_fd *fd) {
@@ -453,13 +456,13 @@
 }
 
 static void close_fd_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
-  fd->closed = 1;
+  fd->closed = true;
   if (!fd->released) {
     close(fd->fd);
   } else {
     remove_fd_from_all_epoll_sets(fd->fd);
   }
-  grpc_exec_ctx_enqueue(exec_ctx, fd->on_done_closure, true, NULL);
+  grpc_exec_ctx_push(exec_ctx, fd->on_done_closure, GRPC_ERROR_NONE, NULL);
 }
 
 static int fd_wrapped_fd(grpc_fd *fd) {
@@ -508,6 +511,14 @@
 static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); }
 #endif
 
+static grpc_error *fd_shutdown_error(bool shutdown) {
+  if (!shutdown) {
+    return GRPC_ERROR_NONE;
+  } else {
+    return GRPC_ERROR_CREATE("FD shutdown");
+  }
+}
+
 static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
                              grpc_closure **st, grpc_closure *closure) {
   if (*st == CLOSURE_NOT_READY) {
@@ -516,7 +527,8 @@
   } else if (*st == CLOSURE_READY) {
     /* already ready ==> queue the closure to run immediately */
     *st = CLOSURE_NOT_READY;
-    grpc_exec_ctx_enqueue(exec_ctx, closure, !fd->shutdown, NULL);
+    grpc_exec_ctx_push(exec_ctx, closure, fd_shutdown_error(fd->shutdown),
+                       NULL);
     maybe_wake_one_watcher_locked(fd);
   } else {
     /* upcallptr was set to a different closure.  This is an error! */
@@ -527,28 +539,28 @@
   }
 }
 
-/* returns 1 if state becomes not ready */
-static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
-                            grpc_closure **st) {
+/* returns true if state becomes not ready */
+static bool set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
+                             grpc_closure **st) {
   if (*st == CLOSURE_READY) {
     /* duplicate ready ==> ignore */
-    return 0;
+    return false;
   } else if (*st == CLOSURE_NOT_READY) {
     /* not ready, and not waiting ==> flag ready */
     *st = CLOSURE_READY;
-    return 0;
+    return false;
   } else {
     /* waiting ==> queue closure */
-    grpc_exec_ctx_enqueue(exec_ctx, *st, !fd->shutdown, NULL);
+    grpc_exec_ctx_push(exec_ctx, *st, fd_shutdown_error(fd->shutdown), NULL);
     *st = CLOSURE_NOT_READY;
-    return 1;
+    return true;
   }
 }
 
 static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
   gpr_mu_lock(&fd->mu);
   GPR_ASSERT(!fd->shutdown);
-  fd->shutdown = 1;
+  fd->shutdown = true;
   set_ready_locked(exec_ctx, fd, &fd->read_closure);
   set_ready_locked(exec_ctx, fd, &fd->write_closure);
   gpr_mu_unlock(&fd->mu);
@@ -621,8 +633,8 @@
 
 static void fd_end_poll(grpc_exec_ctx *exec_ctx, grpc_fd_watcher *watcher,
                         int got_read, int got_write) {
-  int was_polling = 0;
-  int kick = 0;
+  bool was_polling = false;
+  bool kick = false;
   grpc_fd *fd = watcher->fd;
 
   if (fd == NULL) {
@@ -633,17 +645,17 @@
 
   if (watcher == fd->read_watcher) {
     /* remove read watcher, kick if we still need a read */
-    was_polling = 1;
+    was_polling = true;
     if (!got_read) {
-      kick = 1;
+      kick = true;
     }
     fd->read_watcher = NULL;
   }
   if (watcher == fd->write_watcher) {
     /* remove write watcher, kick if we still need a write */
-    was_polling = 1;
+    was_polling = true;
     if (!got_write) {
-      kick = 1;
+      kick = true;
     }
     fd->write_watcher = NULL;
   }
@@ -654,12 +666,12 @@
   }
   if (got_read) {
     if (set_ready_locked(exec_ctx, fd, &fd->read_closure)) {
-      kick = 1;
+      kick = true;
     }
   }
   if (got_write) {
     if (set_ready_locked(exec_ctx, fd, &fd->write_closure)) {
-      kick = 1;
+      kick = true;
     }
   }
   if (kick) {
@@ -717,10 +729,19 @@
   worker->prev->next = worker->next->prev = worker;
 }
 
-static void pollset_kick_ext(grpc_pollset *p,
-                             grpc_pollset_worker *specific_worker,
-                             uint32_t flags) {
+static void kick_append_error(grpc_error **composite, grpc_error *error) {
+  if (error == GRPC_ERROR_NONE) return;
+  if (*composite == GRPC_ERROR_NONE) {
+    *composite = GRPC_ERROR_CREATE("Kick Failure");
+  }
+  *composite = grpc_error_add_child(*composite, error);
+}
+
+static grpc_error *pollset_kick_ext(grpc_pollset *p,
+                                    grpc_pollset_worker *specific_worker,
+                                    uint32_t flags) {
   GPR_TIMER_BEGIN("pollset_kick_ext", 0);
+  grpc_error *error = GRPC_ERROR_NONE;
 
   /* pollset->mu already held */
   if (specific_worker != NULL) {
@@ -730,25 +751,28 @@
       for (specific_worker = p->root_worker.next;
            specific_worker != &p->root_worker;
            specific_worker = specific_worker->next) {
-        grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+        kick_append_error(
+            &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
       }
-      p->kicked_without_pollers = 1;
+      p->kicked_without_pollers = true;
       GPR_TIMER_END("pollset_kick_ext.broadcast", 0);
     } else if (gpr_tls_get(&g_current_thread_worker) !=
                (intptr_t)specific_worker) {
       GPR_TIMER_MARK("different_thread_worker", 0);
       if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
-        specific_worker->reevaluate_polling_on_wakeup = 1;
+        specific_worker->reevaluate_polling_on_wakeup = true;
       }
-      specific_worker->kicked_specifically = 1;
-      grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+      specific_worker->kicked_specifically = true;
+      kick_append_error(&error,
+                        grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
     } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) {
       GPR_TIMER_MARK("kick_yoself", 0);
       if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
-        specific_worker->reevaluate_polling_on_wakeup = 1;
+        specific_worker->reevaluate_polling_on_wakeup = true;
       }
-      specific_worker->kicked_specifically = 1;
-      grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+      specific_worker->kicked_specifically = true;
+      kick_append_error(&error,
+                        grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
     }
   } else if (gpr_tls_get(&g_current_thread_poller) != (intptr_t)p) {
     GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0);
@@ -769,28 +793,30 @@
       if (specific_worker != NULL) {
         GPR_TIMER_MARK("finally_kick", 0);
         push_back_worker(p, specific_worker);
-        grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+        kick_append_error(
+            &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
       }
     } else {
       GPR_TIMER_MARK("kicked_no_pollers", 0);
-      p->kicked_without_pollers = 1;
+      p->kicked_without_pollers = true;
     }
   }
 
   GPR_TIMER_END("pollset_kick_ext", 0);
+  return error;
 }
 
-static void pollset_kick(grpc_pollset *p,
-                         grpc_pollset_worker *specific_worker) {
-  pollset_kick_ext(p, specific_worker, 0);
+static grpc_error *pollset_kick(grpc_pollset *p,
+                                grpc_pollset_worker *specific_worker) {
+  return pollset_kick_ext(p, specific_worker, 0);
 }
 
 /* global state management */
 
-static void pollset_global_init(void) {
+static grpc_error *pollset_global_init(void) {
   gpr_tls_init(&g_current_thread_poller);
   gpr_tls_init(&g_current_thread_worker);
-  grpc_wakeup_fd_init(&grpc_global_wakeup_fd);
+  return grpc_wakeup_fd_init(&grpc_global_wakeup_fd);
 }
 
 static void pollset_global_shutdown(void) {
@@ -799,7 +825,9 @@
   gpr_tls_destroy(&g_current_thread_worker);
 }
 
-static void kick_poller(void) { grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd); }
+static grpc_error *kick_poller(void) {
+  return grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd);
+}
 
 /* main interface */
 
@@ -810,12 +838,11 @@
   *mu = &pollset->mu;
   pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker;
   pollset->in_flight_cbs = 0;
-  pollset->shutting_down = 0;
-  pollset->called_shutdown = 0;
-  pollset->kicked_without_pollers = 0;
+  pollset->shutting_down = false;
+  pollset->called_shutdown = false;
+  pollset->kicked_without_pollers = false;
   pollset->idle_jobs.head = pollset->idle_jobs.tail = NULL;
   pollset->local_wakeup_cache = NULL;
-  pollset->kicked_without_pollers = 0;
   become_basic_pollset(pollset, NULL);
 }
 
@@ -839,9 +866,9 @@
   GPR_ASSERT(!pollset_has_workers(pollset));
   GPR_ASSERT(pollset->idle_jobs.head == pollset->idle_jobs.tail);
   pollset->vtable->destroy(pollset);
-  pollset->shutting_down = 0;
-  pollset->called_shutdown = 0;
-  pollset->kicked_without_pollers = 0;
+  pollset->shutting_down = false;
+  pollset->called_shutdown = false;
+  pollset->kicked_without_pollers = false;
   become_basic_pollset(pollset, NULL);
 }
 
@@ -862,14 +889,15 @@
 static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) {
   GPR_ASSERT(grpc_closure_list_empty(pollset->idle_jobs));
   pollset->vtable->finish_shutdown(pollset);
-  grpc_exec_ctx_enqueue(exec_ctx, pollset->shutdown_done, true, NULL);
+  grpc_exec_ctx_push(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE, NULL);
 }
 
-static void pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                         grpc_pollset_worker **worker_hdl, gpr_timespec now,
-                         gpr_timespec deadline) {
+static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+                                grpc_pollset_worker **worker_hdl,
+                                gpr_timespec now, gpr_timespec deadline) {
   grpc_pollset_worker worker;
   *worker_hdl = &worker;
+  grpc_error *error = GRPC_ERROR_NONE;
 
   /* pollset->mu already held */
   int added_worker = 0;
@@ -879,15 +907,18 @@
   GPR_TIMER_BEGIN("pollset_work", 0);
   /* this must happen before we (potentially) drop pollset->mu */
   worker.next = worker.prev = NULL;
-  worker.reevaluate_polling_on_wakeup = 0;
+  worker.reevaluate_polling_on_wakeup = false;
   if (pollset->local_wakeup_cache != NULL) {
     worker.wakeup_fd = pollset->local_wakeup_cache;
     pollset->local_wakeup_cache = worker.wakeup_fd->next;
   } else {
     worker.wakeup_fd = gpr_malloc(sizeof(*worker.wakeup_fd));
-    grpc_wakeup_fd_init(&worker.wakeup_fd->fd);
+    error = grpc_wakeup_fd_init(&worker.wakeup_fd->fd);
+    if (error != GRPC_ERROR_NONE) {
+      return error;
+    }
   }
-  worker.kicked_specifically = 0;
+  worker.kicked_specifically = false;
   /* If there's work waiting for the pollset to be idle, and the
      pollset is idle, then do that work */
   if (!pollset_has_workers(pollset) &&
@@ -922,14 +953,14 @@
       }
       gpr_tls_set(&g_current_thread_poller, (intptr_t)pollset);
       GPR_TIMER_BEGIN("maybe_work_and_unlock", 0);
-      pollset->vtable->maybe_work_and_unlock(exec_ctx, pollset, &worker,
-                                             deadline, now);
+      error = pollset->vtable->maybe_work_and_unlock(exec_ctx, pollset, &worker,
+                                                     deadline, now);
       GPR_TIMER_END("maybe_work_and_unlock", 0);
       locked = 0;
       gpr_tls_set(&g_current_thread_poller, 0);
     } else {
       GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0);
-      pollset->kicked_without_pollers = 0;
+      pollset->kicked_without_pollers = false;
     }
   /* Finished execution - start cleaning up.
      Note that we may arrive here from outside the enclosing while() loop.
@@ -944,9 +975,9 @@
     /* If we're forced to re-evaluate polling (via pollset_kick with
        GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force
        a loop */
-    if (worker.reevaluate_polling_on_wakeup) {
-      worker.reevaluate_polling_on_wakeup = 0;
-      pollset->kicked_without_pollers = 0;
+    if (worker.reevaluate_polling_on_wakeup && error == GRPC_ERROR_NONE) {
+      worker.reevaluate_polling_on_wakeup = false;
+      pollset->kicked_without_pollers = false;
       if (queued_work || worker.kicked_specifically) {
         /* If there's queued work on the list, then set the deadline to be
            immediate so we get back out of the polling loop quickly */
@@ -967,7 +998,7 @@
     if (pollset_has_workers(pollset)) {
       pollset_kick(pollset, NULL);
     } else if (!pollset->called_shutdown && pollset->in_flight_cbs == 0) {
-      pollset->called_shutdown = 1;
+      pollset->called_shutdown = true;
       gpr_mu_unlock(&pollset->mu);
       finish_shutdown(exec_ctx, pollset);
       grpc_exec_ctx_flush(exec_ctx);
@@ -985,12 +1016,13 @@
   }
   *worker_hdl = NULL;
   GPR_TIMER_END("pollset_work", 0);
+  return error;
 }
 
 static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                              grpc_closure *closure) {
   GPR_ASSERT(!pollset->shutting_down);
-  pollset->shutting_down = 1;
+  pollset->shutting_down = true;
   pollset->shutdown_done = closure;
   pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
   if (!pollset_has_workers(pollset)) {
@@ -998,7 +1030,7 @@
   }
   if (!pollset->called_shutdown && pollset->in_flight_cbs == 0 &&
       !pollset_has_workers(pollset)) {
-    pollset->called_shutdown = 1;
+    pollset->called_shutdown = true;
     finish_shutdown(exec_ctx, pollset);
   }
 }
@@ -1033,7 +1065,7 @@
 } grpc_unary_promote_args;
 
 static void basic_do_promote(grpc_exec_ctx *exec_ctx, void *args,
-                             bool success) {
+                             grpc_error *error) {
   grpc_unary_promote_args *up_args = args;
   const grpc_pollset_vtable *original_vtable = up_args->original_vtable;
   grpc_pollset *pollset = up_args->pollset;
@@ -1062,7 +1094,7 @@
   if (pollset->shutting_down) {
     /* We don't care about this pollset anymore. */
     if (pollset->in_flight_cbs == 0 && !pollset->called_shutdown) {
-      pollset->called_shutdown = 1;
+      pollset->called_shutdown = true;
       finish_shutdown(exec_ctx, pollset);
     }
   } else if (fd_is_orphaned(fd)) {
@@ -1135,7 +1167,8 @@
   up_args->promotion_closure.cb = basic_do_promote;
   up_args->promotion_closure.cb_arg = up_args;
 
-  grpc_closure_list_add(&pollset->idle_jobs, &up_args->promotion_closure, 1);
+  grpc_closure_list_append(&pollset->idle_jobs, &up_args->promotion_closure,
+                           GRPC_ERROR_NONE);
   pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
 
 exit:
@@ -1144,11 +1177,19 @@
   }
 }
 
-static void basic_pollset_maybe_work_and_unlock(grpc_exec_ctx *exec_ctx,
-                                                grpc_pollset *pollset,
-                                                grpc_pollset_worker *worker,
-                                                gpr_timespec deadline,
-                                                gpr_timespec now) {
+static void work_combine_error(grpc_error **composite, grpc_error *error) {
+  if (error == GRPC_ERROR_NONE) return;
+  if (*composite == GRPC_ERROR_NONE) {
+    *composite = GRPC_ERROR_CREATE("pollset_work");
+  }
+  *composite = grpc_error_add_child(*composite, error);
+}
+
+static grpc_error *basic_pollset_maybe_work_and_unlock(
+    grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker,
+    gpr_timespec deadline, gpr_timespec now) {
+  grpc_error *error = GRPC_ERROR_NONE;
+
 #define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR)
 #define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR)
 
@@ -1198,7 +1239,7 @@
 
   if (r < 0) {
     if (errno != EINTR) {
-      gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno));
+      work_combine_error(&error, GRPC_OS_ERROR(errno, "poll"));
     }
     if (fd) {
       fd_end_poll(exec_ctx, &fd_watcher, 0, 0);
@@ -1209,10 +1250,12 @@
     }
   } else {
     if (pfd[0].revents & POLLIN_CHECK) {
-      grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd);
+      work_combine_error(&error,
+                         grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd));
     }
     if (pfd[1].revents & POLLIN_CHECK) {
-      grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd);
+      work_combine_error(&error,
+                         grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd));
     }
     if (nfds > 2) {
       fd_end_poll(exec_ctx, &fd_watcher, pfd[2].revents & POLLIN_CHECK,
@@ -1225,6 +1268,8 @@
   if (fd) {
     GRPC_FD_UNREF(fd, "basicpoll_begin");
   }
+
+  return error;
 }
 
 static void basic_pollset_destroy(grpc_pollset *pollset) {
@@ -1285,9 +1330,11 @@
   }
 }
 
-static void multipoll_with_poll_pollset_maybe_work_and_unlock(
+static grpc_error *multipoll_with_poll_pollset_maybe_work_and_unlock(
     grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker,
     gpr_timespec deadline, gpr_timespec now) {
+  grpc_error *error = GRPC_ERROR_NONE;
+
 #define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR)
 #define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR)
 
@@ -1351,7 +1398,7 @@
 
   if (r < 0) {
     if (errno != EINTR) {
-      gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno));
+      work_combine_error(&error, GRPC_OS_ERROR(errno, "poll"));
     }
     for (i = 2; i < pfd_count; i++) {
       fd_end_poll(exec_ctx, &watchers[i], 0, 0);
@@ -1362,10 +1409,12 @@
     }
   } else {
     if (pfds[0].revents & POLLIN_CHECK) {
-      grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd);
+      work_combine_error(&error,
+                         grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd));
     }
     if (pfds[1].revents & POLLIN_CHECK) {
-      grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd);
+      work_combine_error(&error,
+                         grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd));
     }
     for (i = 2; i < pfd_count; i++) {
       if (watchers[i].fd == NULL) {
@@ -1379,6 +1428,7 @@
 
   gpr_free(pfds);
   gpr_free(watchers);
+  return error;
 }
 
 static void multipoll_with_poll_pollset_finish_shutdown(grpc_pollset *pollset) {
@@ -1558,7 +1608,7 @@
 }
 
 static void perform_delayed_add(grpc_exec_ctx *exec_ctx, void *arg,
-                                bool iomgr_status) {
+                                grpc_error *error) {
   delayed_add *da = arg;
 
   if (!fd_is_orphaned(da->fd)) {
@@ -1570,8 +1620,9 @@
   if (da->pollset->shutting_down) {
     /* We don't care about this pollset anymore. */
     if (da->pollset->in_flight_cbs == 0 && !da->pollset->called_shutdown) {
-      da->pollset->called_shutdown = 1;
-      grpc_exec_ctx_enqueue(exec_ctx, da->pollset->shutdown_done, true, NULL);
+      da->pollset->called_shutdown = true;
+      grpc_exec_ctx_push(exec_ctx, da->pollset->shutdown_done, GRPC_ERROR_NONE,
+                         NULL);
     }
   }
   gpr_mu_unlock(&da->pollset->mu);
@@ -1595,14 +1646,14 @@
     GRPC_FD_REF(fd, "delayed_add");
     grpc_closure_init(&da->closure, perform_delayed_add, da);
     pollset->in_flight_cbs++;
-    grpc_exec_ctx_enqueue(exec_ctx, &da->closure, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, &da->closure, GRPC_ERROR_NONE, NULL);
   }
 }
 
 /* TODO(klempner): We probably want to turn this down a bit */
 #define GRPC_EPOLL_MAX_EVENTS 1000
 
-static void multipoll_with_epoll_pollset_maybe_work_and_unlock(
+static grpc_error *multipoll_with_epoll_pollset_maybe_work_and_unlock(
     grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker,
     gpr_timespec deadline, gpr_timespec now) {
   struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS];
@@ -1611,6 +1662,7 @@
   epoll_hdr *h = pollset->data.ptr;
   int timeout_ms;
   struct pollfd pfds[2];
+  grpc_error *error = GRPC_ERROR_NONE;
 
   /* If you want to ignore epoll's ability to sanely handle parallel pollers,
    * for a more apples-to-apples performance comparison with poll, add a
@@ -1639,13 +1691,14 @@
 
   if (poll_rv < 0) {
     if (errno != EINTR) {
-      gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno));
+      work_combine_error(&error, GRPC_OS_ERROR(errno, "poll"));
     }
   } else if (poll_rv == 0) {
     /* do nothing */
   } else {
     if (pfds[0].revents) {
-      grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd);
+      work_combine_error(&error,
+                         grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd));
     }
     if (pfds[1].revents) {
       do {
@@ -1653,7 +1706,7 @@
         ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, 0);
         if (ep_rv < 0) {
           if (errno != EINTR) {
-            gpr_log(GPR_ERROR, "epoll_wait() failed: %s", strerror(errno));
+            work_combine_error(&error, GRPC_OS_ERROR(errno, "epoll_wait"));
           }
         } else {
           int i;
@@ -1665,7 +1718,8 @@
             int read_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI);
             int write_ev = ep_ev[i].events & EPOLLOUT;
             if (fd == NULL) {
-              grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd);
+              work_combine_error(&error, grpc_wakeup_fd_consume_wakeup(
+                                             &grpc_global_wakeup_fd));
             } else {
               if (read_ev || cancel) {
                 fd_become_readable(exec_ctx, fd);
@@ -1679,6 +1733,7 @@
       } while (ep_rv == GRPC_EPOLL_MAX_EVENTS);
     }
   }
+  return error;
 }
 
 static void multipoll_with_epoll_pollset_finish_shutdown(
@@ -1921,14 +1976,23 @@
 };
 
 const grpc_event_engine_vtable *grpc_init_poll_and_epoll_posix(void) {
+  const char *msg;
+  grpc_error *err = GRPC_ERROR_NONE;
 #ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL
   platform_become_multipoller = epoll_become_multipoller;
 #else
   platform_become_multipoller = poll_become_multipoller;
 #endif
   fd_global_init();
-  pollset_global_init();
+  err = pollset_global_init();
+  if (err != GRPC_ERROR_NONE) goto error;
   return &vtable;
+
+error:
+  msg = grpc_error_string(err);
+  gpr_log(GPR_ERROR, "%s", msg);
+  grpc_error_free_string(msg);
+  return NULL;
 }
 
 #endif
diff --git a/src/core/lib/iomgr/ev_poll_posix.c b/src/core/lib/iomgr/ev_poll_posix.c
index e91ae40..b964b88 100644
--- a/src/core/lib/iomgr/ev_poll_posix.c
+++ b/src/core/lib/iomgr/ev_poll_posix.c
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015-2016, Google Inc.
+ * Copyright 2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -218,9 +218,9 @@
 #define GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP 2
 /* As per pollset_kick, with an extended set of flags (defined above)
    -- mostly for fd_posix's use. */
-static void pollset_kick_ext(grpc_pollset *p,
-                             grpc_pollset_worker *specific_worker,
-                             uint32_t flags);
+static grpc_error *pollset_kick_ext(grpc_pollset *p,
+                                    grpc_pollset_worker *specific_worker,
+                                    uint32_t flags) GRPC_MUST_USE_RESULT;
 
 /* Return 1 if the pollset has active threads in pollset_work (pollset must
  * be locked) */
@@ -316,12 +316,13 @@
   return (gpr_atm_acq_load(&fd->refst) & 1) == 0;
 }
 
-static void pollset_kick_locked(grpc_fd_watcher *watcher) {
+static grpc_error *pollset_kick_locked(grpc_fd_watcher *watcher) {
   gpr_mu_lock(&watcher->pollset->mu);
   GPR_ASSERT(watcher->worker);
-  pollset_kick_ext(watcher->pollset, watcher->worker,
-                   GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP);
+  grpc_error *err = pollset_kick_ext(watcher->pollset, watcher->worker,
+                                     GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP);
   gpr_mu_unlock(&watcher->pollset->mu);
+  return err;
 }
 
 static void maybe_wake_one_watcher_locked(grpc_fd *fd) {
@@ -358,7 +359,7 @@
   if (!fd->released) {
     close(fd->fd);
   }
-  grpc_exec_ctx_enqueue(exec_ctx, fd->on_done_closure, true, NULL);
+  grpc_exec_ctx_push(exec_ctx, fd->on_done_closure, GRPC_ERROR_NONE, NULL);
 }
 
 static int fd_wrapped_fd(grpc_fd *fd) {
@@ -407,6 +408,14 @@
 static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); }
 #endif
 
+static grpc_error *fd_shutdown_error(bool shutdown) {
+  if (!shutdown) {
+    return GRPC_ERROR_NONE;
+  } else {
+    return GRPC_ERROR_CREATE("FD shutdown");
+  }
+}
+
 static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
                              grpc_closure **st, grpc_closure *closure) {
   if (*st == CLOSURE_NOT_READY) {
@@ -415,7 +424,8 @@
   } else if (*st == CLOSURE_READY) {
     /* already ready ==> queue the closure to run immediately */
     *st = CLOSURE_NOT_READY;
-    grpc_exec_ctx_enqueue(exec_ctx, closure, !fd->shutdown, NULL);
+    grpc_exec_ctx_push(exec_ctx, closure, fd_shutdown_error(fd->shutdown),
+                       NULL);
     maybe_wake_one_watcher_locked(fd);
   } else {
     /* upcallptr was set to a different closure.  This is an error! */
@@ -438,7 +448,7 @@
     return 0;
   } else {
     /* waiting ==> queue closure */
-    grpc_exec_ctx_enqueue(exec_ctx, *st, !fd->shutdown, NULL);
+    grpc_exec_ctx_push(exec_ctx, *st, fd_shutdown_error(fd->shutdown), NULL);
     *st = CLOSURE_NOT_READY;
     return 1;
   }
@@ -610,10 +620,19 @@
   worker->prev->next = worker->next->prev = worker;
 }
 
-static void pollset_kick_ext(grpc_pollset *p,
-                             grpc_pollset_worker *specific_worker,
-                             uint32_t flags) {
+static void kick_append_error(grpc_error **composite, grpc_error *error) {
+  if (error == GRPC_ERROR_NONE) return;
+  if (*composite == GRPC_ERROR_NONE) {
+    *composite = GRPC_ERROR_CREATE("Kick Failure");
+  }
+  *composite = grpc_error_add_child(*composite, error);
+}
+
+static grpc_error *pollset_kick_ext(grpc_pollset *p,
+                                    grpc_pollset_worker *specific_worker,
+                                    uint32_t flags) {
   GPR_TIMER_BEGIN("pollset_kick_ext", 0);
+  grpc_error *error = GRPC_ERROR_NONE;
 
   /* pollset->mu already held */
   if (specific_worker != NULL) {
@@ -623,25 +642,28 @@
       for (specific_worker = p->root_worker.next;
            specific_worker != &p->root_worker;
            specific_worker = specific_worker->next) {
-        grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+        kick_append_error(
+            &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
       }
-      p->kicked_without_pollers = 1;
+      p->kicked_without_pollers = true;
       GPR_TIMER_END("pollset_kick_ext.broadcast", 0);
     } else if (gpr_tls_get(&g_current_thread_worker) !=
                (intptr_t)specific_worker) {
       GPR_TIMER_MARK("different_thread_worker", 0);
       if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
-        specific_worker->reevaluate_polling_on_wakeup = 1;
+        specific_worker->reevaluate_polling_on_wakeup = true;
       }
-      specific_worker->kicked_specifically = 1;
-      grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+      specific_worker->kicked_specifically = true;
+      kick_append_error(&error,
+                        grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
     } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) {
       GPR_TIMER_MARK("kick_yoself", 0);
       if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) {
-        specific_worker->reevaluate_polling_on_wakeup = 1;
+        specific_worker->reevaluate_polling_on_wakeup = true;
       }
-      specific_worker->kicked_specifically = 1;
-      grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+      specific_worker->kicked_specifically = true;
+      kick_append_error(&error,
+                        grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
     }
   } else if (gpr_tls_get(&g_current_thread_poller) != (intptr_t)p) {
     GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0);
@@ -662,28 +684,30 @@
       if (specific_worker != NULL) {
         GPR_TIMER_MARK("finally_kick", 0);
         push_back_worker(p, specific_worker);
-        grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd);
+        kick_append_error(
+            &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd));
       }
     } else {
       GPR_TIMER_MARK("kicked_no_pollers", 0);
-      p->kicked_without_pollers = 1;
+      p->kicked_without_pollers = true;
     }
   }
 
   GPR_TIMER_END("pollset_kick_ext", 0);
+  return error;
 }
 
-static void pollset_kick(grpc_pollset *p,
-                         grpc_pollset_worker *specific_worker) {
-  pollset_kick_ext(p, specific_worker, 0);
+static grpc_error *pollset_kick(grpc_pollset *p,
+                                grpc_pollset_worker *specific_worker) {
+  return pollset_kick_ext(p, specific_worker, 0);
 }
 
 /* global state management */
 
-static void pollset_global_init(void) {
+static grpc_error *pollset_global_init(void) {
   gpr_tls_init(&g_current_thread_poller);
   gpr_tls_init(&g_current_thread_worker);
-  grpc_wakeup_fd_init(&grpc_global_wakeup_fd);
+  return grpc_wakeup_fd_init(&grpc_global_wakeup_fd);
 }
 
 static void pollset_global_shutdown(void) {
@@ -692,7 +716,9 @@
   gpr_tls_destroy(&g_current_thread_worker);
 }
 
-static void kick_poller(void) { grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd); }
+static grpc_error *kick_poller(void) {
+  return grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd);
+}
 
 /* main interface */
 
@@ -774,14 +800,23 @@
   }
   pollset->fd_count = 0;
   pollset->del_count = 0;
-  grpc_exec_ctx_enqueue(exec_ctx, pollset->shutdown_done, true, NULL);
+  grpc_exec_ctx_push(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE, NULL);
 }
 
-static void pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                         grpc_pollset_worker **worker_hdl, gpr_timespec now,
-                         gpr_timespec deadline) {
+static void work_combine_error(grpc_error **composite, grpc_error *error) {
+  if (error == GRPC_ERROR_NONE) return;
+  if (*composite == GRPC_ERROR_NONE) {
+    *composite = GRPC_ERROR_CREATE("pollset_work");
+  }
+  *composite = grpc_error_add_child(*composite, error);
+}
+
+static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+                                grpc_pollset_worker **worker_hdl,
+                                gpr_timespec now, gpr_timespec deadline) {
   grpc_pollset_worker worker;
   *worker_hdl = &worker;
+  grpc_error *error = GRPC_ERROR_NONE;
 
   /* pollset->mu already held */
   int added_worker = 0;
@@ -797,7 +832,10 @@
     pollset->local_wakeup_cache = worker.wakeup_fd->next;
   } else {
     worker.wakeup_fd = gpr_malloc(sizeof(*worker.wakeup_fd));
-    grpc_wakeup_fd_init(&worker.wakeup_fd->fd);
+    error = grpc_wakeup_fd_init(&worker.wakeup_fd->fd);
+    if (error != GRPC_ERROR_NONE) {
+      return error;
+    }
   }
   worker.kicked_specifically = 0;
   /* If there's work waiting for the pollset to be idle, and the
@@ -896,7 +934,7 @@
 
       if (r < 0) {
         if (errno != EINTR) {
-          gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno));
+          work_combine_error(&error, GRPC_OS_ERROR(errno, "poll"));
         }
         for (i = 2; i < pfd_count; i++) {
           fd_end_poll(exec_ctx, &watchers[i], 0, 0);
@@ -907,10 +945,12 @@
         }
       } else {
         if (pfds[0].revents & POLLIN_CHECK) {
-          grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd);
+          work_combine_error(
+              &error, grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd));
         }
         if (pfds[1].revents & POLLIN_CHECK) {
-          grpc_wakeup_fd_consume_wakeup(&worker.wakeup_fd->fd);
+          work_combine_error(
+              &error, grpc_wakeup_fd_consume_wakeup(&worker.wakeup_fd->fd));
         }
         for (i = 2; i < pfd_count; i++) {
           if (watchers[i].fd == NULL) {
@@ -943,7 +983,7 @@
     /* If we're forced to re-evaluate polling (via pollset_kick with
        GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force
        a loop */
-    if (worker.reevaluate_polling_on_wakeup) {
+    if (worker.reevaluate_polling_on_wakeup && error == GRPC_ERROR_NONE) {
       worker.reevaluate_polling_on_wakeup = 0;
       pollset->kicked_without_pollers = 0;
       if (queued_work || worker.kicked_specifically) {
@@ -988,6 +1028,7 @@
   }
   *worker_hdl = NULL;
   GPR_TIMER_END("pollset_work", 0);
+  return error;
 }
 
 static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
diff --git a/src/core/lib/iomgr/ev_posix.c b/src/core/lib/iomgr/ev_posix.c
index a7dfc95..63b207d 100644
--- a/src/core/lib/iomgr/ev_posix.c
+++ b/src/core/lib/iomgr/ev_posix.c
@@ -182,15 +182,15 @@
   g_event_engine->pollset_destroy(pollset);
 }
 
-void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                       grpc_pollset_worker **worker, gpr_timespec now,
-                       gpr_timespec deadline) {
-  g_event_engine->pollset_work(exec_ctx, pollset, worker, now, deadline);
+grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+                              grpc_pollset_worker **worker, gpr_timespec now,
+                              gpr_timespec deadline) {
+  return g_event_engine->pollset_work(exec_ctx, pollset, worker, now, deadline);
 }
 
-void grpc_pollset_kick(grpc_pollset *pollset,
-                       grpc_pollset_worker *specific_worker) {
-  g_event_engine->pollset_kick(pollset, specific_worker);
+grpc_error *grpc_pollset_kick(grpc_pollset *pollset,
+                              grpc_pollset_worker *specific_worker) {
+  return g_event_engine->pollset_kick(pollset, specific_worker);
 }
 
 void grpc_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
@@ -240,6 +240,6 @@
   g_event_engine->pollset_set_del_fd(exec_ctx, pollset_set, fd);
 }
 
-void grpc_kick_poller(void) { g_event_engine->kick_poller(); }
+grpc_error *grpc_kick_poller(void) { return g_event_engine->kick_poller(); }
 
 #endif  // GPR_POSIX_SOCKET
diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h
index 1fa9f5e..c9f9fd9 100644
--- a/src/core/lib/iomgr/ev_posix.h
+++ b/src/core/lib/iomgr/ev_posix.h
@@ -61,11 +61,11 @@
                            grpc_closure *closure);
   void (*pollset_reset)(grpc_pollset *pollset);
   void (*pollset_destroy)(grpc_pollset *pollset);
-  void (*pollset_work)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                       grpc_pollset_worker **worker, gpr_timespec now,
-                       gpr_timespec deadline);
-  void (*pollset_kick)(grpc_pollset *pollset,
-                       grpc_pollset_worker *specific_worker);
+  grpc_error *(*pollset_work)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+                              grpc_pollset_worker **worker, gpr_timespec now,
+                              gpr_timespec deadline);
+  grpc_error *(*pollset_kick)(grpc_pollset *pollset,
+                              grpc_pollset_worker *specific_worker);
   void (*pollset_add_fd)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
                          struct grpc_fd *fd);
 
@@ -88,7 +88,7 @@
   void (*pollset_set_del_fd)(grpc_exec_ctx *exec_ctx,
                              grpc_pollset_set *pollset_set, grpc_fd *fd);
 
-  void (*kick_poller)(void);
+  grpc_error *(*kick_poller)(void);
 
   void (*shutdown_engine)(void);
 } grpc_event_engine_vtable;
diff --git a/src/core/lib/iomgr/exec_ctx.c b/src/core/lib/iomgr/exec_ctx.c
index e451479..fcf39ff 100644
--- a/src/core/lib/iomgr/exec_ctx.c
+++ b/src/core/lib/iomgr/exec_ctx.c
@@ -63,11 +63,12 @@
     grpc_closure *c = exec_ctx->closure_list.head;
     exec_ctx->closure_list.head = exec_ctx->closure_list.tail = NULL;
     while (c != NULL) {
-      bool success = (bool)(c->final_data & 1);
-      grpc_closure *next = (grpc_closure *)(c->final_data & ~(uintptr_t)1);
+      grpc_closure *next = c->next_data.next;
+      grpc_error *error = c->error;
       did_something = true;
       GPR_TIMER_BEGIN("grpc_exec_ctx_flush.cb", 0);
-      c->cb(exec_ctx, c->cb_arg, success);
+      c->cb(exec_ctx, c->cb_arg, error);
+      GRPC_ERROR_UNREF(error);
       GPR_TIMER_END("grpc_exec_ctx_flush.cb", 0);
       c = next;
     }
@@ -81,11 +82,11 @@
   grpc_exec_ctx_flush(exec_ctx);
 }
 
-void grpc_exec_ctx_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
-                           bool success,
-                           grpc_workqueue *offload_target_or_null) {
+void grpc_exec_ctx_push(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+                        grpc_error *error,
+                        grpc_workqueue *offload_target_or_null) {
   GPR_ASSERT(offload_target_or_null == NULL);
-  grpc_closure_list_add(&exec_ctx->closure_list, closure, success);
+  grpc_closure_list_append(&exec_ctx->closure_list, closure, error);
 }
 
 void grpc_exec_ctx_enqueue_list(grpc_exec_ctx *exec_ctx,
diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h
index 9d47a26..617f48f 100644
--- a/src/core/lib/iomgr/exec_ctx.h
+++ b/src/core/lib/iomgr/exec_ctx.h
@@ -94,9 +94,9 @@
  *  the instance is destroyed, or work may be lost. */
 void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx);
 /** Add a closure to be executed at the next flush/finish point */
-void grpc_exec_ctx_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
-                           bool success,
-                           grpc_workqueue *offload_target_or_null);
+void grpc_exec_ctx_push(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
+                        grpc_error *error,
+                        grpc_workqueue *offload_target_or_null);
 /** Returns true if we'd like to leave this execution context as soon as
     possible: useful for deciding whether to do something more or not depending
     on outside context */
diff --git a/src/core/lib/iomgr/executor.c b/src/core/lib/iomgr/executor.c
index 36e22e4..8d7535d 100644
--- a/src/core/lib/iomgr/executor.c
+++ b/src/core/lib/iomgr/executor.c
@@ -112,10 +112,10 @@
   g_executor.pending_join = 1;
 }
 
-void grpc_executor_enqueue(grpc_closure *closure, bool success) {
+void grpc_executor_push(grpc_closure *closure, grpc_error *error) {
   gpr_mu_lock(&g_executor.mu);
   if (g_executor.shutting_down == 0) {
-    grpc_closure_list_add(&g_executor.closures, closure, success);
+    grpc_closure_list_append(&g_executor.closures, closure, error);
     maybe_spawn_locked();
   }
   gpr_mu_unlock(&g_executor.mu);
diff --git a/src/core/lib/iomgr/executor.h b/src/core/lib/iomgr/executor.h
index b7e6f51..da9dcd0 100644
--- a/src/core/lib/iomgr/executor.h
+++ b/src/core/lib/iomgr/executor.h
@@ -45,7 +45,7 @@
 
 /** Enqueue \a closure for its eventual execution of \a f(arg) on a separate
  * thread */
-void grpc_executor_enqueue(grpc_closure *closure, bool success);
+void grpc_executor_push(grpc_closure *closure, grpc_error *error);
 
 /** Shutdown the executor, running all pending work as part of the call */
 void grpc_executor_shutdown();
diff --git a/src/core/lib/iomgr/iocp_windows.c b/src/core/lib/iomgr/iocp_windows.c
index d46558a..47bafd9 100644
--- a/src/core/lib/iomgr/iocp_windows.c
+++ b/src/core/lib/iomgr/iocp_windows.c
@@ -121,7 +121,7 @@
     info->has_pending_iocp = 1;
   }
   gpr_mu_unlock(&socket->state_mu);
-  grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL);
+  grpc_exec_ctx_push(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
   return GRPC_IOCP_WORK_WORK;
 }
 
@@ -187,7 +187,7 @@
   gpr_mu_lock(&socket->state_mu);
   if (info->has_pending_iocp) {
     info->has_pending_iocp = 0;
-    grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
   } else {
     info->closure = closure;
   }
diff --git a/src/core/lib/iomgr/pollset.h b/src/core/lib/iomgr/pollset.h
index c40a474..8d9edc8 100644
--- a/src/core/lib/iomgr/pollset.h
+++ b/src/core/lib/iomgr/pollset.h
@@ -81,14 +81,15 @@
    May call grpc_closure_list_run on grpc_closure_list, without holding the
    pollset
    lock */
-void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                       grpc_pollset_worker **worker, gpr_timespec now,
-                       gpr_timespec deadline);
+grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+                              grpc_pollset_worker **worker, gpr_timespec now,
+                              gpr_timespec deadline) GRPC_MUST_USE_RESULT;
 
 /* Break one polling thread out of polling work for this pollset.
    If specific_worker is GRPC_POLLSET_KICK_BROADCAST, kick ALL the workers.
    Otherwise, if specific_worker is non-NULL, then kick that worker. */
-void grpc_pollset_kick(grpc_pollset *pollset,
-                       grpc_pollset_worker *specific_worker);
+grpc_error *grpc_pollset_kick(grpc_pollset *pollset,
+                              grpc_pollset_worker *specific_worker)
+    GRPC_MUST_USE_RESULT;
 
 #endif /* GRPC_CORE_LIB_IOMGR_POLLSET_H */
diff --git a/src/core/lib/iomgr/pollset_windows.c b/src/core/lib/iomgr/pollset_windows.c
index bff5c58..5882d8d 100644
--- a/src/core/lib/iomgr/pollset_windows.c
+++ b/src/core/lib/iomgr/pollset_windows.c
@@ -109,7 +109,7 @@
   pollset->shutting_down = 1;
   grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST);
   if (!pollset->is_iocp_worker) {
-    grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
   } else {
     pollset->on_shutdown = closure;
   }
@@ -127,9 +127,9 @@
   pollset->on_shutdown = NULL;
 }
 
-void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
-                       grpc_pollset_worker **worker_hdl, gpr_timespec now,
-                       gpr_timespec deadline) {
+grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
+                              grpc_pollset_worker **worker_hdl,
+                              gpr_timespec now, gpr_timespec deadline) {
   grpc_pollset_worker worker;
   *worker_hdl = &worker;
 
@@ -167,7 +167,8 @@
       }
 
       if (pollset->shutting_down && pollset->on_shutdown != NULL) {
-        grpc_exec_ctx_enqueue(exec_ctx, pollset->on_shutdown, true, NULL);
+        grpc_exec_ctx_push(exec_ctx, pollset->on_shutdown, GRPC_ERROR_NONE,
+                           NULL);
         pollset->on_shutdown = NULL;
       }
       goto done;
@@ -197,9 +198,11 @@
   }
   gpr_cv_destroy(&worker.cv);
   *worker_hdl = NULL;
+  return GRPC_ERROR_NONE;
 }
 
-void grpc_pollset_kick(grpc_pollset *p, grpc_pollset_worker *specific_worker) {
+grpc_error *grpc_pollset_kick(grpc_pollset *p,
+                              grpc_pollset_worker *specific_worker) {
   if (specific_worker != NULL) {
     if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) {
       for (specific_worker =
@@ -233,6 +236,7 @@
       p->kicked_without_pollers = 1;
     }
   }
+  return GRPC_ERROR_NONE;
 }
 
 void grpc_kick_poller(void) { grpc_iocp_kick(); }
diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h
index ef198fe..ddbe375 100644
--- a/src/core/lib/iomgr/resolve_address.h
+++ b/src/core/lib/iomgr/resolve_address.h
@@ -50,24 +50,20 @@
   grpc_resolved_address *addrs;
 } grpc_resolved_addresses;
 
-/* Async result callback:
-   On success: addresses is the result, and the callee must call
-   grpc_resolved_addresses_destroy when it's done with them
-   On failure: addresses is NULL */
-typedef void (*grpc_resolve_cb)(grpc_exec_ctx *exec_ctx, void *arg,
-                                grpc_resolved_addresses *addresses);
 /* Asynchronously resolve addr. Use default_port if a port isn't designated
    in addr, otherwise use the port in addr. */
 /* TODO(ctiller): add a timeout here */
 extern void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *addr,
                                     const char *default_port,
-                                    grpc_resolve_cb cb, void *arg);
+                                    grpc_closure *on_done,
+                                    grpc_resolved_addresses **addresses);
 /* Destroy resolved addresses */
 void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addresses);
 
 /* Resolve addr in a blocking fashion. Returns NULL on failure. On success,
    result must be freed with grpc_resolved_addresses_destroy. */
-extern grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
-    const char *name, const char *default_port);
+extern grpc_error *(*grpc_blocking_resolve_address)(
+    const char *name, const char *default_port,
+    grpc_resolved_addresses **addresses);
 
 #endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H */
diff --git a/src/core/lib/iomgr/resolve_address_posix.c b/src/core/lib/iomgr/resolve_address_posix.c
index cae91ee..9a320dc 100644
--- a/src/core/lib/iomgr/resolve_address_posix.c
+++ b/src/core/lib/iomgr/resolve_address_posix.c
@@ -54,38 +54,33 @@
 #include "src/core/lib/support/block_annotate.h"
 #include "src/core/lib/support/string.h"
 
-typedef struct {
-  char *name;
-  char *default_port;
-  grpc_resolve_cb cb;
-  grpc_closure request_closure;
-  void *arg;
-} request;
-
-static grpc_resolved_addresses *blocking_resolve_address_impl(
-    const char *name, const char *default_port) {
+static grpc_error *blocking_resolve_address_impl(
+    const char *name, const char *default_port,
+    grpc_resolved_addresses **addresses) {
   struct addrinfo hints;
   struct addrinfo *result = NULL, *resp;
   char *host;
   char *port;
   int s;
   size_t i;
-  grpc_resolved_addresses *addrs = NULL;
+  grpc_error *err;
 
   if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' &&
       name[4] == ':' && name[5] != 0) {
-    return grpc_resolve_unix_domain_address(name + 5);
+    return grpc_resolve_unix_domain_address(name + 5, addresses);
   }
 
   /* parse name, splitting it into host and port parts */
   gpr_split_host_port(name, &host, &port);
   if (host == NULL) {
-    gpr_log(GPR_ERROR, "unparseable host:port: '%s'", name);
+    err = grpc_error_set_str(GRPC_ERROR_CREATE("unparseable host:port"),
+                             GRPC_ERROR_STR_TARGET_ADDRESS, name);
     goto done;
   }
   if (port == NULL) {
     if (default_port == NULL) {
-      gpr_log(GPR_ERROR, "no port in name '%s'", name);
+      err = grpc_error_set_str(GRPC_ERROR_CREATE("no port in name"),
+                               GRPC_ERROR_STR_TARGET_ADDRESS, name);
       goto done;
     }
     port = gpr_strdup(default_port);
@@ -115,23 +110,31 @@
   }
 
   if (s != 0) {
-    gpr_log(GPR_ERROR, "getaddrinfo: %s", gai_strerror(s));
+    err = grpc_error_set_str(
+        grpc_error_set_str(
+            grpc_error_set_str(grpc_error_set_int(GRPC_ERROR_CREATE("OS Error"),
+                                                  GRPC_ERROR_INT_ERRNO, s),
+                               GRPC_ERROR_STR_OS_ERROR, gai_strerror(s)),
+            GRPC_ERROR_STR_SYSCALL, "getaddrinfo"),
+        GRPC_ERROR_STR_TARGET_ADDRESS, name);
     goto done;
   }
 
   /* Success path: set addrs non-NULL, fill it in */
-  addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
-  addrs->naddrs = 0;
+  *addresses = gpr_malloc(sizeof(grpc_resolved_addresses));
+  (*addresses)->naddrs = 0;
   for (resp = result; resp != NULL; resp = resp->ai_next) {
-    addrs->naddrs++;
+    (*addresses)->naddrs++;
   }
-  addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address) * addrs->naddrs);
+  (*addresses)->addrs =
+      gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs);
   i = 0;
   for (resp = result; resp != NULL; resp = resp->ai_next) {
-    memcpy(&addrs->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
-    addrs->addrs[i].len = resp->ai_addrlen;
+    memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
+    (*addresses)->addrs[i].len = resp->ai_addrlen;
     i++;
   }
+  err = GRPC_ERROR_NONE;
 
 done:
   gpr_free(host);
@@ -139,45 +142,59 @@
   if (result) {
     freeaddrinfo(result);
   }
-  return addrs;
+  return err;
 }
 
-grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
-    const char *name, const char *default_port) = blocking_resolve_address_impl;
+grpc_error *(*grpc_blocking_resolve_address)(
+    const char *name, const char *default_port,
+    grpc_resolved_addresses **addresses) = blocking_resolve_address_impl;
+
+typedef struct {
+  char *name;
+  char *default_port;
+  grpc_closure *on_done;
+  grpc_resolved_addresses **addrs_out;
+  grpc_closure request_closure;
+  void *arg;
+} request;
 
 /* Callback to be passed to grpc_executor to asynch-ify
  * grpc_blocking_resolve_address */
-static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, bool success) {
+static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp,
+                              grpc_error *error) {
   request *r = rp;
-  grpc_resolved_addresses *resolved =
-      grpc_blocking_resolve_address(r->name, r->default_port);
-  void *arg = r->arg;
-  grpc_resolve_cb cb = r->cb;
+  grpc_exec_ctx_push(
+      exec_ctx, r->on_done,
+      grpc_blocking_resolve_address(r->name, r->default_port, r->addrs_out),
+      NULL);
   gpr_free(r->name);
   gpr_free(r->default_port);
-  cb(exec_ctx, arg, resolved);
   gpr_free(r);
 }
 
 void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) {
-  gpr_free(addrs->addrs);
+  if (addrs != NULL) {
+    gpr_free(addrs->addrs);
+  }
   gpr_free(addrs);
 }
 
 static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
-                                 const char *default_port, grpc_resolve_cb cb,
-                                 void *arg) {
+                                 const char *default_port,
+                                 grpc_closure *on_done,
+                                 grpc_resolved_addresses **addrs) {
   request *r = gpr_malloc(sizeof(request));
   grpc_closure_init(&r->request_closure, do_request_thread, r);
   r->name = gpr_strdup(name);
   r->default_port = gpr_strdup(default_port);
-  r->cb = cb;
-  r->arg = arg;
-  grpc_executor_enqueue(&r->request_closure, 1);
+  r->on_done = on_done;
+  r->addrs_out = addrs;
+  grpc_executor_push(&r->request_closure, GRPC_ERROR_NONE);
 }
 
 void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
-                             const char *default_port, grpc_resolve_cb cb,
-                             void *arg) = resolve_address_impl;
+                             const char *default_port, grpc_closure *on_done,
+                             grpc_resolved_addresses **addrs) =
+    resolve_address_impl;
 
 #endif
diff --git a/src/core/lib/iomgr/resolve_address_windows.c b/src/core/lib/iomgr/resolve_address_windows.c
index 9147362..22a9feb 100644
--- a/src/core/lib/iomgr/resolve_address_windows.c
+++ b/src/core/lib/iomgr/resolve_address_windows.c
@@ -56,30 +56,37 @@
 typedef struct {
   char *name;
   char *default_port;
-  grpc_resolve_cb cb;
   grpc_closure request_closure;
-  void *arg;
+  grpc_closure *on_done;
+  grpc_resolved_addresses **addresses;
 } request;
 
-static grpc_resolved_addresses *blocking_resolve_address_impl(
-    const char *name, const char *default_port) {
+static grpc_error *blocking_resolve_address_impl(
+    const char *name, const char *default_port,
+    grpc_resolved_addresses **addresses) {
   struct addrinfo hints;
   struct addrinfo *result = NULL, *resp;
   char *host;
   char *port;
   int s;
   size_t i;
-  grpc_resolved_addresses *addrs = NULL;
+  grpc_error *error = GRPC_ERROR_NONE;
 
   /* parse name, splitting it into host and port parts */
   gpr_split_host_port(name, &host, &port);
   if (host == NULL) {
-    gpr_log(GPR_ERROR, "unparseable host:port: '%s'", name);
+    char *msg;
+    gpr_asprintf(&msg, "unparseable host:port: '%s'", name);
+    error = GRPC_ERROR_CREATE(msg);
+    gpr_free(msg);
     goto done;
   }
   if (port == NULL) {
     if (default_port == NULL) {
-      gpr_log(GPR_ERROR, "no port in name '%s'", name);
+      char *msg;
+      gpr_asprintf(&msg, "no port in name '%s'", name);
+      error = GRPC_ERROR_CREATE(msg);
+      gpr_free(msg);
       goto done;
     }
     port = gpr_strdup(default_port);
@@ -95,31 +102,30 @@
   s = getaddrinfo(host, port, &hints, &result);
   GRPC_SCHEDULING_END_BLOCKING_REGION;
   if (s != 0) {
-    char *error_message = gpr_format_message(s);
-    gpr_log(GPR_ERROR, "getaddrinfo: %s", error_message);
-    gpr_free(error_message);
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "getaddrinfo");
     goto done;
   }
 
   /* Success path: set addrs non-NULL, fill it in */
-  addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
-  addrs->naddrs = 0;
+  (*addresses) = gpr_malloc(sizeof(grpc_resolved_addresses));
+  (*addresses)->naddrs = 0;
   for (resp = result; resp != NULL; resp = resp->ai_next) {
-    addrs->naddrs++;
+    (*addresses)->naddrs++;
   }
-  addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address) * addrs->naddrs);
+  (*addresses)->addrs =
+      gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs);
   i = 0;
   for (resp = result; resp != NULL; resp = resp->ai_next) {
-    memcpy(&addrs->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
-    addrs->addrs[i].len = resp->ai_addrlen;
+    memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen);
+    (*addresses)->addrs[i].len = resp->ai_addrlen;
     i++;
   }
 
   {
-    for (i = 0; i < addrs->naddrs; i++) {
+    for (i = 0; i < (*addresses)->naddrs; i++) {
       char *buf;
-      grpc_sockaddr_to_string(&buf, (struct sockaddr *)&addrs->addrs[i].addr,
-                              0);
+      grpc_sockaddr_to_string(
+          &buf, (struct sockaddr *)&(*addresses)->addrs[i].addr, 0);
       gpr_free(buf);
     }
   }
@@ -130,45 +136,53 @@
   if (result) {
     freeaddrinfo(result);
   }
-  return addrs;
+  return error;
 }
 
-grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
-    const char *name, const char *default_port) = blocking_resolve_address_impl;
+grpc_error *(*grpc_blocking_resolve_address)(
+    const char *name, const char *default_port,
+    grpc_resolved_addresses **addresses) = blocking_resolve_address_impl;
 
 /* Callback to be passed to grpc_executor to asynch-ify
  * grpc_blocking_resolve_address */
-static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, bool success) {
+static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp,
+                              grpc_error *error) {
   request *r = rp;
-  grpc_resolved_addresses *resolved =
-      grpc_blocking_resolve_address(r->name, r->default_port);
-  void *arg = r->arg;
-  grpc_resolve_cb cb = r->cb;
+  if (error == GRPC_ERROR_NONE) {
+    error =
+        grpc_blocking_resolve_address(r->name, r->default_port, r->addresses);
+  } else {
+    GRPC_ERROR_REF(error);
+  }
+  grpc_exec_ctx_push(exec_ctx, r->on_done, error, NULL);
   gpr_free(r->name);
   gpr_free(r->default_port);
-  cb(exec_ctx, arg, resolved);
   gpr_free(r);
 }
 
 void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) {
-  gpr_free(addrs->addrs);
+  if (addrs != NULL) {
+    gpr_free(addrs->addrs);
+  }
   gpr_free(addrs);
 }
 
 static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name,
-                                 const char *default_port, grpc_resolve_cb cb,
-                                 void *arg) {
+                                 const char *default_port,
+                                 grpc_closure *on_done,
+                                 grpc_resolved_addresses **addresses) {
   request *r = gpr_malloc(sizeof(request));
   grpc_closure_init(&r->request_closure, do_request_thread, r);
   r->name = gpr_strdup(name);
   r->default_port = gpr_strdup(default_port);
-  r->cb = cb;
-  r->arg = arg;
-  grpc_executor_enqueue(&r->request_closure, 1);
+  r->on_done = on_done;
+  r->addresses = addresses;
+  grpc_executor_push(&r->request_closure, GRPC_ERROR_NONE);
 }
 
 void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name,
-                             const char *default_port, grpc_resolve_cb cb,
-                             void *arg) = resolve_address_impl;
+                             const char *default_port, grpc_closure *on_done,
+                             grpc_resolved_addresses **addresses) =
+    resolve_address_impl;
 
 #endif
diff --git a/src/core/lib/iomgr/socket_utils_common_posix.c b/src/core/lib/iomgr/socket_utils_common_posix.c
index fa83cee..d1721c9 100644
--- a/src/core/lib/iomgr/socket_utils_common_posix.c
+++ b/src/core/lib/iomgr/socket_utils_common_posix.c
@@ -57,10 +57,10 @@
 #include "src/core/lib/support/string.h"
 
 /* set a socket to non blocking mode */
-int grpc_set_socket_nonblocking(int fd, int non_blocking) {
+grpc_error *grpc_set_socket_nonblocking(int fd, int non_blocking) {
   int oldflags = fcntl(fd, F_GETFL, 0);
   if (oldflags < 0) {
-    return 0;
+    return GRPC_OS_ERROR(errno, "fcntl");
   }
 
   if (non_blocking) {
@@ -70,52 +70,57 @@
   }
 
   if (fcntl(fd, F_SETFL, oldflags) != 0) {
-    return 0;
+    return GRPC_OS_ERROR(errno, "fcntl");
   }
 
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
-int grpc_set_socket_no_sigpipe_if_possible(int fd) {
+grpc_error *grpc_set_socket_no_sigpipe_if_possible(int fd) {
 #ifdef GPR_HAVE_SO_NOSIGPIPE
   int val = 1;
   int newval;
   socklen_t intlen = sizeof(newval);
-  return 0 == setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)) &&
-         0 == getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen) &&
-         (newval != 0) == val;
-#else
-  return 1;
+  if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(SO_NOSIGPIPE)");
+  }
+  if (0 != getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen)) {
+    return GRPC_OS_ERROR(errno, "getsockopt(SO_NOSIGPIPE)");
+  }
+  if ((newval != 0) != (val != 0)) {
+    return GRPC_ERROR_CREATE("Failed to set SO_NOSIGPIPE");
+  }
 #endif
+  return GRPC_ERROR_NONE;
 }
 
-int grpc_set_socket_ip_pktinfo_if_possible(int fd) {
+grpc_error *grpc_set_socket_ip_pktinfo_if_possible(int fd) {
 #ifdef GPR_HAVE_IP_PKTINFO
   int get_local_ip = 1;
-  return 0 == setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip,
-                         sizeof(get_local_ip));
-#else
-  (void)fd;
-  return 1;
+  if (0 != setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip,
+                      sizeof(get_local_ip))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(IP_PKTINFO)");
+  }
 #endif
+  return GRPC_ERROR_NONE;
 }
 
-int grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) {
+grpc_error *grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) {
 #ifdef GPR_HAVE_IPV6_RECVPKTINFO
   int get_local_ip = 1;
-  return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip,
-                         sizeof(get_local_ip));
-#else
-  (void)fd;
-  return 1;
+  if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip,
+                      sizeof(get_local_ip))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(IPV6_RECVPKTINFO)");
+  }
 #endif
+  return GRPC_ERROR_NONE;
 }
 
 /* set a socket to close on exec */
-int grpc_set_socket_cloexec(int fd, int close_on_exec) {
+grpc_error *grpc_set_socket_cloexec(int fd, int close_on_exec) {
   int oldflags = fcntl(fd, F_GETFD, 0);
   if (oldflags < 0) {
-    return 0;
+    return GRPC_OS_ERROR(errno, "fcntl");
   }
 
   if (close_on_exec) {
@@ -125,30 +130,45 @@
   }
 
   if (fcntl(fd, F_SETFD, oldflags) != 0) {
-    return 0;
+    return GRPC_OS_ERROR(errno, "fcntl");
   }
 
-  return 1;
+  return GRPC_ERROR_NONE;
 }
 
 /* set a socket to reuse old addresses */
-int grpc_set_socket_reuse_addr(int fd, int reuse) {
+grpc_error *grpc_set_socket_reuse_addr(int fd, int reuse) {
   int val = (reuse != 0);
   int newval;
   socklen_t intlen = sizeof(newval);
-  return 0 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) &&
-         0 == getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen) &&
-         (newval != 0) == val;
+  if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEADDR)");
+  }
+  if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen)) {
+    return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEADDR)");
+  }
+  if ((newval != 0) != val) {
+    return GRPC_ERROR_CREATE("Failed to set SO_REUSEADDR");
+  }
+
+  return GRPC_ERROR_NONE;
 }
 
 /* disable nagle */
-int grpc_set_socket_low_latency(int fd, int low_latency) {
+grpc_error *grpc_set_socket_low_latency(int fd, int low_latency) {
   int val = (low_latency != 0);
   int newval;
   socklen_t intlen = sizeof(newval);
-  return 0 == setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) &&
-         0 == getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen) &&
-         (newval != 0) == val;
+  if (0 != setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) {
+    return GRPC_OS_ERROR(errno, "setsockopt(TCP_NODELAY)");
+  }
+  if (0 != getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen)) {
+    return GRPC_OS_ERROR(errno, "getsockopt(TCP_NODELAY)");
+  }
+  if ((newval != 0) != val) {
+    return GRPC_ERROR_CREATE("Failed to set TCP_NODELAY");
+  }
+  return GRPC_ERROR_NONE;
 }
 
 static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT;
@@ -196,35 +216,40 @@
   }
 }
 
-int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
-                                 int protocol, grpc_dualstack_mode *dsmode) {
+grpc_error *grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
+                                         int protocol,
+                                         grpc_dualstack_mode *dsmode,
+                                         int *newfd) {
   int family = addr->sa_family;
   if (family == AF_INET6) {
-    int fd;
     if (grpc_ipv6_loopback_available()) {
-      fd = socket(family, type, protocol);
+      *newfd = socket(family, type, protocol);
     } else {
-      fd = -1;
+      *newfd = -1;
       errno = EAFNOSUPPORT;
     }
     /* Check if we've got a valid dualstack socket. */
-    if (fd >= 0 && set_socket_dualstack(fd)) {
+    if (*newfd >= 0 && set_socket_dualstack(*newfd)) {
       *dsmode = GRPC_DSMODE_DUALSTACK;
-      return fd;
+      return GRPC_ERROR_NONE;
     }
     /* If this isn't an IPv4 address, then return whatever we've got. */
     if (!grpc_sockaddr_is_v4mapped(addr, NULL)) {
       *dsmode = GRPC_DSMODE_IPV6;
-      return fd;
+      return GRPC_ERROR_NONE;
     }
     /* Fall back to AF_INET. */
-    if (fd >= 0) {
-      close(fd);
+    if (*newfd >= 0) {
+      close(*newfd);
     }
     family = AF_INET;
   }
   *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE;
-  return socket(family, type, protocol);
+  *newfd = socket(family, type, protocol);
+  if (*newfd == -1) {
+    return GRPC_OS_ERROR(errno, "socket");
+  }
+  return GRPC_ERROR_NONE;
 }
 
 #endif
diff --git a/src/core/lib/iomgr/socket_utils_posix.h b/src/core/lib/iomgr/socket_utils_posix.h
index a8f6e5e..4e66cbc 100644
--- a/src/core/lib/iomgr/socket_utils_posix.h
+++ b/src/core/lib/iomgr/socket_utils_posix.h
@@ -37,21 +37,23 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
+#include "src/core/lib/iomgr/error.h"
+
 /* a wrapper for accept or accept4 */
 int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
                  int nonblock, int cloexec);
 
 /* set a socket to non blocking mode */
-int grpc_set_socket_nonblocking(int fd, int non_blocking);
+grpc_error *grpc_set_socket_nonblocking(int fd, int non_blocking);
 
 /* set a socket to close on exec */
-int grpc_set_socket_cloexec(int fd, int close_on_exec);
+grpc_error *grpc_set_socket_cloexec(int fd, int close_on_exec);
 
 /* set a socket to reuse old addresses */
-int grpc_set_socket_reuse_addr(int fd, int reuse);
+grpc_error *grpc_set_socket_reuse_addr(int fd, int reuse);
 
 /* disable nagle */
-int grpc_set_socket_low_latency(int fd, int low_latency);
+grpc_error *grpc_set_socket_low_latency(int fd, int low_latency);
 
 /* Returns true if this system can create AF_INET6 sockets bound to ::1.
    The value is probed once, and cached for the life of the process.
@@ -66,17 +68,17 @@
 /* Tries to set SO_NOSIGPIPE if available on this platform.
    Returns 1 on success, 0 on failure.
    If SO_NO_SIGPIPE is not available, returns 1. */
-int grpc_set_socket_no_sigpipe_if_possible(int fd);
+grpc_error *grpc_set_socket_no_sigpipe_if_possible(int fd);
 
 /* Tries to set IP_PKTINFO if available on this platform.
    Returns 1 on success, 0 on failure.
    If IP_PKTINFO is not available, returns 1. */
-int grpc_set_socket_ip_pktinfo_if_possible(int fd);
+grpc_error *grpc_set_socket_ip_pktinfo_if_possible(int fd);
 
 /* Tries to set IPV6_RECVPKTINFO if available on this platform.
    Returns 1 on success, 0 on failure.
    If IPV6_RECVPKTINFO is not available, returns 1. */
-int grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd);
+grpc_error *grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd);
 
 /* An enum to keep track of IPv4/IPv6 socket modes.
 
@@ -117,7 +119,9 @@
      IPv4, so that bind() or connect() see the correct family.
    Also, it's important to distinguish between DUALSTACK and IPV6 when
    listening on the [::] wildcard address. */
-int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
-                                 int protocol, grpc_dualstack_mode *dsmode);
+grpc_error *grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
+                                         int protocol,
+                                         grpc_dualstack_mode *dsmode,
+                                         int *newfd);
 
 #endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H */
diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c
index e93d573..9c4ca16 100644
--- a/src/core/lib/iomgr/tcp_client_posix.c
+++ b/src/core/lib/iomgr/tcp_client_posix.c
@@ -71,33 +71,39 @@
   grpc_closure *closure;
 } async_connect;
 
-static int prepare_socket(const struct sockaddr *addr, int fd) {
-  if (fd < 0) {
-    goto error;
-  }
+static grpc_error *prepare_socket(const struct sockaddr *addr, int fd) {
+  grpc_error *err = GRPC_ERROR_NONE;
 
-  if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) ||
-      (!grpc_is_unix_socket(addr) && !grpc_set_socket_low_latency(fd, 1)) ||
-      !grpc_set_socket_no_sigpipe_if_possible(fd)) {
-    gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd,
-            strerror(errno));
-    goto error;
+  GPR_ASSERT(fd >= 0);
+
+  err = grpc_set_socket_nonblocking(fd, 1);
+  if (err != GRPC_ERROR_NONE) goto error;
+  err = grpc_set_socket_cloexec(fd, 1);
+  if (err != GRPC_ERROR_NONE) goto error;
+  if (!grpc_is_unix_socket(addr)) {
+    err = grpc_set_socket_low_latency(fd, 1);
+    if (err != GRPC_ERROR_NONE) goto error;
   }
-  return 1;
+  err = grpc_set_socket_no_sigpipe_if_possible(fd);
+  if (err != GRPC_ERROR_NONE) goto error;
+  goto done;
 
 error:
   if (fd >= 0) {
     close(fd);
   }
-  return 0;
+done:
+  return err;
 }
 
-static void tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, bool success) {
+static void tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
   int done;
   async_connect *ac = acp;
   if (grpc_tcp_trace) {
-    gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: success=%d", ac->addr_str,
-            success);
+    const char *str = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", ac->addr_str,
+            str);
+    grpc_error_free_string(str);
   }
   gpr_mu_lock(&ac->mu);
   if (ac->fd != NULL) {
@@ -112,7 +118,7 @@
   }
 }
 
-static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, bool success) {
+static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
   async_connect *ac = acp;
   int so_error = 0;
   socklen_t so_error_size;
@@ -122,9 +128,13 @@
   grpc_closure *closure = ac->closure;
   grpc_fd *fd;
 
+  GRPC_ERROR_REF(error);
+
   if (grpc_tcp_trace) {
-    gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: success=%d",
-            ac->addr_str, success);
+    const char *str = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: error=%s",
+            ac->addr_str, str);
+    grpc_error_free_string(str);
   }
 
   gpr_mu_lock(&ac->mu);
@@ -136,15 +146,14 @@
   grpc_timer_cancel(exec_ctx, &ac->alarm);
 
   gpr_mu_lock(&ac->mu);
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     do {
       so_error_size = sizeof(so_error);
       err = getsockopt(grpc_fd_wrapped_fd(fd), SOL_SOCKET, SO_ERROR, &so_error,
                        &so_error_size);
     } while (err < 0 && errno == EINTR);
     if (err < 0) {
-      gpr_log(GPR_ERROR, "failed to connect to '%s': getsockopt(ERROR): %s",
-              ac->addr_str, strerror(errno));
+      error = GRPC_OS_ERROR(errno, "getsockopt");
       goto finish;
     } else if (so_error != 0) {
       if (so_error == ENOBUFS) {
@@ -169,14 +178,12 @@
       } else {
         switch (so_error) {
           case ECONNREFUSED:
-            gpr_log(
-                GPR_ERROR,
-                "failed to connect to '%s': socket error: connection refused",
-                ac->addr_str);
+            error = grpc_error_set_int(error, GRPC_ERROR_INT_ERRNO, errno);
+            error = grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR,
+                                       "Connection refused");
             break;
           default:
-            gpr_log(GPR_ERROR, "failed to connect to '%s': socket error: %d",
-                    ac->addr_str, so_error);
+            error = GRPC_OS_ERROR(errno, "getsockopt(SO_ERROR)");
             break;
         }
         goto finish;
@@ -188,8 +195,8 @@
       goto finish;
     }
   } else {
-    gpr_log(GPR_ERROR, "failed to connect to '%s': timeout occurred",
-            ac->addr_str);
+    error =
+        grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, "Timeout occurred");
     goto finish;
   }
 
@@ -203,12 +210,18 @@
   }
   done = (--ac->refs == 0);
   gpr_mu_unlock(&ac->mu);
+  if (error != GRPC_ERROR_NONE) {
+    error = grpc_error_set_str(error, GRPC_ERROR_STR_DESCRIPTION,
+                               "Failed to connect to remote host");
+    error =
+        grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, ac->addr_str);
+  }
   if (done) {
     gpr_mu_destroy(&ac->mu);
     gpr_free(ac->addr_str);
     gpr_free(ac);
   }
-  grpc_exec_ctx_enqueue(exec_ctx, closure, *ep != NULL, NULL);
+  grpc_exec_ctx_push(exec_ctx, closure, error, NULL);
 }
 
 static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx,
@@ -225,6 +238,7 @@
   grpc_fd *fdobj;
   char *name;
   char *addr_str;
+  grpc_error *error;
 
   *ep = NULL;
 
@@ -234,9 +248,10 @@
     addr_len = sizeof(addr6_v4mapped);
   }
 
-  fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
-  if (fd < 0) {
-    gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
+  error = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd);
+  if (error != GRPC_ERROR_NONE) {
+    grpc_exec_ctx_push(exec_ctx, closure, error, NULL);
+    return;
   }
   if (dsmode == GRPC_DSMODE_IPV4) {
     /* If we got an AF_INET socket, map the address back to IPv4. */
@@ -244,8 +259,8 @@
     addr = (struct sockaddr *)&addr4_copy;
     addr_len = sizeof(addr4_copy);
   }
-  if (!prepare_socket(addr, fd)) {
-    grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL);
+  if ((error = prepare_socket(addr, fd)) != GRPC_ERROR_NONE) {
+    grpc_exec_ctx_push(exec_ctx, closure, error, NULL);
     return;
   }
 
@@ -261,14 +276,14 @@
 
   if (err >= 0) {
     *ep = grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str);
-    grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, closure, GRPC_ERROR_NONE, NULL);
     goto done;
   }
 
   if (errno != EWOULDBLOCK && errno != EINPROGRESS) {
-    gpr_log(GPR_ERROR, "connect error to '%s': %s", addr_str, strerror(errno));
     grpc_fd_orphan(exec_ctx, fdobj, NULL, NULL, "tcp_client_connect_error");
-    grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, closure, GRPC_OS_ERROR(errno, "connect"),
+                       NULL);
     goto done;
   }
 
diff --git a/src/core/lib/iomgr/tcp_client_windows.c b/src/core/lib/iomgr/tcp_client_windows.c
index 66f9ff7..a645e83 100644
--- a/src/core/lib/iomgr/tcp_client_windows.c
+++ b/src/core/lib/iomgr/tcp_client_windows.c
@@ -75,7 +75,7 @@
   if (socket != NULL) grpc_winsocket_destroy(socket);
 }
 
-static void on_alarm(grpc_exec_ctx *exec_ctx, void *acp, bool occured) {
+static void on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
   async_connect *ac = acp;
   gpr_mu_lock(&ac->mu);
   if (ac->socket != NULL) {
@@ -84,7 +84,7 @@
   async_connect_unlock_and_cleanup(ac, ac->socket);
 }
 
-static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, bool from_iocp) {
+static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) {
   async_connect *ac = acp;
   SOCKET sock = ac->socket->socket;
   grpc_endpoint **ep = ac->endpoint;
@@ -92,6 +92,8 @@
   grpc_winsocket_callback_info *info = &ac->socket->write_info;
   grpc_closure *on_done = ac->on_done;
 
+  GRPC_ERROR_REF(error);
+
   gpr_mu_lock(&ac->mu);
   grpc_winsocket *socket = ac->socket;
   ac->socket = NULL;
@@ -101,17 +103,14 @@
 
   gpr_mu_lock(&ac->mu);
 
-  if (from_iocp && socket != NULL) {
+  if (error == GRPC_ERROR_NONE && socket != NULL) {
     DWORD transfered_bytes = 0;
     DWORD flags;
     BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
                                               &transfered_bytes, FALSE, &flags);
     GPR_ASSERT(transfered_bytes == 0);
     if (!wsa_success) {
-      char *utf8_message = gpr_format_message(WSAGetLastError());
-      gpr_log(GPR_ERROR, "on_connect error connecting to '%s': %s",
-              ac->addr_name, utf8_message);
-      gpr_free(utf8_message);
+      error = GRPC_WSA_ERROR(WSAGetLastError(), "ConnectEx");
     } else {
       *ep = grpc_tcp_create(socket, ac->addr_name);
       socket = NULL;
@@ -121,7 +120,7 @@
   async_connect_unlock_and_cleanup(ac, socket);
   /* If the connection was aborted, the callback was already called when
      the deadline was met. */
-  on_done->cb(exec_ctx, on_done->cb_arg, *ep != NULL);
+  grpc_exec_ctx_push(exec_ctx, on_done, error, NULL);
 }
 
 /* Tries to issue one async connection, then schedules both an IOCP
@@ -141,10 +140,8 @@
   LPFN_CONNECTEX ConnectEx;
   GUID guid = WSAID_CONNECTEX;
   DWORD ioctl_num_bytes;
-  const char *message = NULL;
-  char *utf8_message;
   grpc_winsocket_callback_info *info;
-  int last_error;
+  grpc_error *error = GRPC_ERROR_NONE;
 
   *endpoint = NULL;
 
@@ -157,12 +154,12 @@
   sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
                    WSA_FLAG_OVERLAPPED);
   if (sock == INVALID_SOCKET) {
-    message = "Unable to create socket: %s";
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
     goto failure;
   }
 
-  if (!grpc_tcp_prepare_socket(sock)) {
-    message = "Unable to set socket options: %s";
+  error = grpc_tcp_prepare_socket(sock);
+  if (error != GRPC_ERROR_NONE) {
     goto failure;
   }
 
@@ -173,7 +170,8 @@
                &ConnectEx, sizeof(ConnectEx), &ioctl_num_bytes, NULL, NULL);
 
   if (status != 0) {
-    message = "Unable to retrieve ConnectEx pointer: %s";
+    error = GRPC_WSA_ERROR(WSAGetLastError(),
+                           "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)");
     goto failure;
   }
 
@@ -181,7 +179,7 @@
 
   status = bind(sock, (struct sockaddr *)&local_address, sizeof(local_address));
   if (status != 0) {
-    message = "Unable to bind socket: %s";
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "bind");
     goto failure;
   }
 
@@ -193,9 +191,9 @@
   /* It wouldn't be unusual to get a success immediately. But we'll still get
      an IOCP notification, so let's ignore it. */
   if (!success) {
-    int error = WSAGetLastError();
-    if (error != ERROR_IO_PENDING) {
-      message = "ConnectEx failed: %s";
+    int last_error = WSAGetLastError();
+    if (last_error != ERROR_IO_PENDING) {
+      error = GRPC_WSA_ERROR(last_error, "ConnectEx");
       goto failure;
     }
   }
@@ -215,17 +213,18 @@
   return;
 
 failure:
-  last_error = WSAGetLastError();
-  utf8_message = gpr_format_message(last_error);
-  gpr_log(GPR_ERROR, message, utf8_message);
-  gpr_log(GPR_ERROR, "last error = %d", last_error);
-  gpr_free(utf8_message);
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  char *target_uri = grpc_sockaddr_to_uri(addr);
+  grpc_error *final_error = grpc_error_set_str(
+      GRPC_ERROR_CREATE_REFERENCING("Failed to connect", &error, 1),
+      GRPC_ERROR_STR_TARGET_ADDRESS, target_uri);
+  GRPC_ERROR_UNREF(error);
   if (socket != NULL) {
     grpc_winsocket_destroy(socket);
   } else if (sock != INVALID_SOCKET) {
     closesocket(sock);
   }
-  grpc_exec_ctx_enqueue(exec_ctx, on_done, false, NULL);
+  grpc_exec_ctx_push(exec_ctx, on_done, final_error, NULL);
 }
 
 #endif /* GPR_WINSOCK_SOCKET */
diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c
index e286922..48a19be 100644
--- a/src/core/lib/iomgr/tcp_posix.c
+++ b/src/core/lib/iomgr/tcp_posix.c
@@ -38,6 +38,7 @@
 #include "src/core/lib/iomgr/tcp_posix.h"
 
 #include <errno.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -74,7 +75,7 @@
   grpc_endpoint base;
   grpc_fd *em_fd;
   int fd;
-  int finished_edge;
+  bool finished_edge;
   msg_iovlen_type iov_size; /* Number of slices to allocate per read attempt */
   size_t slice_size;
   gpr_refcount refcount;
@@ -101,9 +102,9 @@
 } grpc_tcp;
 
 static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
-                            bool success);
+                            grpc_error *error);
 static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
-                             bool success);
+                             grpc_error *error);
 
 static void tcp_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) {
   grpc_tcp *tcp = (grpc_tcp *)ep;
@@ -155,12 +156,15 @@
   TCP_UNREF(exec_ctx, tcp, "destroy");
 }
 
-static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, int success) {
+static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp,
+                         grpc_error *error) {
   grpc_closure *cb = tcp->read_cb;
 
   if (grpc_tcp_trace) {
     size_t i;
-    gpr_log(GPR_DEBUG, "read: success=%d", success);
+    const char *str = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "read: error=%s", str);
+    grpc_error_free_string(str);
     for (i = 0; i < tcp->incoming_buffer->count; i++) {
       char *dump = gpr_dump_slice(tcp->incoming_buffer->slices[i],
                                   GPR_DUMP_HEX | GPR_DUMP_ASCII);
@@ -171,7 +175,7 @@
 
   tcp->read_cb = NULL;
   tcp->incoming_buffer = NULL;
-  cb->cb(exec_ctx, cb->cb_arg, success);
+  grpc_exec_ctx_push(exec_ctx, cb, error, NULL);
 }
 
 #define MAX_READ_IOVEC 4
@@ -219,15 +223,14 @@
       /* We've consumed the edge, request a new one */
       grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_closure);
     } else {
-      /* TODO(klempner): Log interesting errors */
       gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer);
-      call_read_cb(exec_ctx, tcp, 0);
+      call_read_cb(exec_ctx, tcp, GRPC_OS_ERROR(errno, "recvmsg"));
       TCP_UNREF(exec_ctx, tcp, "read");
     }
   } else if (read_bytes == 0) {
     /* 0 read size ==> end of stream */
     gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer);
-    call_read_cb(exec_ctx, tcp, 0);
+    call_read_cb(exec_ctx, tcp, GRPC_ERROR_CREATE("EOF"));
     TCP_UNREF(exec_ctx, tcp, "read");
   } else {
     GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length);
@@ -240,7 +243,7 @@
       ++tcp->iov_size;
     }
     GPR_ASSERT((size_t)read_bytes == tcp->incoming_buffer->length);
-    call_read_cb(exec_ctx, tcp, 1);
+    call_read_cb(exec_ctx, tcp, GRPC_ERROR_NONE);
     TCP_UNREF(exec_ctx, tcp, "read");
   }
 
@@ -248,13 +251,13 @@
 }
 
 static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
-                            bool success) {
+                            grpc_error *error) {
   grpc_tcp *tcp = (grpc_tcp *)arg;
   GPR_ASSERT(!tcp->finished_edge);
 
-  if (!success) {
+  if (error != GRPC_ERROR_NONE) {
     gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer);
-    call_read_cb(exec_ctx, tcp, 0);
+    call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error));
     TCP_UNREF(exec_ctx, tcp, "read");
   } else {
     tcp_continue_read(exec_ctx, tcp);
@@ -271,17 +274,16 @@
   gpr_slice_buffer_swap(incoming_buffer, &tcp->last_read_buffer);
   TCP_REF(tcp, "read");
   if (tcp->finished_edge) {
-    tcp->finished_edge = 0;
+    tcp->finished_edge = false;
     grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_closure);
   } else {
-    grpc_exec_ctx_enqueue(exec_ctx, &tcp->read_closure, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, &tcp->read_closure, GRPC_ERROR_NONE, NULL);
   }
 }
 
-typedef enum { FLUSH_DONE, FLUSH_PENDING, FLUSH_ERROR } flush_result;
-
+/* returns true if done, false if pending; if returning true, *error is set */
 #define MAX_WRITE_IOVEC 16
-static flush_result tcp_flush(grpc_tcp *tcp) {
+static bool tcp_flush(grpc_tcp *tcp, grpc_error **error) {
   struct msghdr msg;
   struct iovec iov[MAX_WRITE_IOVEC];
   msg_iovlen_type iov_size;
@@ -331,10 +333,10 @@
       if (errno == EAGAIN) {
         tcp->outgoing_slice_idx = unwind_slice_idx;
         tcp->outgoing_byte_idx = unwind_byte_idx;
-        return FLUSH_PENDING;
+        return false;
       } else {
-        /* TODO(klempner): Log some of these */
-        return FLUSH_ERROR;
+        *error = GRPC_OS_ERROR(errno, "sendmsg");
+        return true;
       }
     }
 
@@ -355,42 +357,42 @@
     }
 
     if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) {
-      return FLUSH_DONE;
+      *error = GRPC_ERROR_NONE;
+      return true;
     }
   };
 }
 
 static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */,
-                             bool success) {
+                             grpc_error *error) {
   grpc_tcp *tcp = (grpc_tcp *)arg;
-  flush_result status;
   grpc_closure *cb;
 
-  if (!success) {
+  if (error != GRPC_ERROR_NONE) {
     cb = tcp->write_cb;
     tcp->write_cb = NULL;
-    cb->cb(exec_ctx, cb->cb_arg, 0);
+    cb->cb(exec_ctx, cb->cb_arg, error);
     TCP_UNREF(exec_ctx, tcp, "write");
     return;
   }
 
-  status = tcp_flush(tcp);
-  if (status == FLUSH_PENDING) {
+  if (!tcp_flush(tcp, &error)) {
     grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_closure);
   } else {
     cb = tcp->write_cb;
     tcp->write_cb = NULL;
     GPR_TIMER_BEGIN("tcp_handle_write.cb", 0);
-    cb->cb(exec_ctx, cb->cb_arg, status == FLUSH_DONE);
+    cb->cb(exec_ctx, cb->cb_arg, error);
     GPR_TIMER_END("tcp_handle_write.cb", 0);
     TCP_UNREF(exec_ctx, tcp, "write");
+    GRPC_ERROR_UNREF(error);
   }
 }
 
 static void tcp_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
                       gpr_slice_buffer *buf, grpc_closure *cb) {
   grpc_tcp *tcp = (grpc_tcp *)ep;
-  flush_result status;
+  grpc_error *error = GRPC_ERROR_NONE;
 
   if (grpc_tcp_trace) {
     size_t i;
@@ -408,20 +410,19 @@
 
   if (buf->length == 0) {
     GPR_TIMER_END("tcp_write", 0);
-    grpc_exec_ctx_enqueue(exec_ctx, cb, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, cb, GRPC_ERROR_NONE, NULL);
     return;
   }
   tcp->outgoing_buffer = buf;
   tcp->outgoing_slice_idx = 0;
   tcp->outgoing_byte_idx = 0;
 
-  status = tcp_flush(tcp);
-  if (status == FLUSH_PENDING) {
+  if (!tcp_flush(tcp, &error)) {
     TCP_REF(tcp, "write");
     tcp->write_cb = cb;
     grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_closure);
   } else {
-    grpc_exec_ctx_enqueue(exec_ctx, cb, status == FLUSH_DONE, NULL);
+    grpc_exec_ctx_push(exec_ctx, cb, error, NULL);
   }
 
   GPR_TIMER_END("tcp_write", 0);
@@ -461,7 +462,7 @@
   tcp->incoming_buffer = NULL;
   tcp->slice_size = slice_size;
   tcp->iov_size = 1;
-  tcp->finished_edge = 1;
+  tcp->finished_edge = true;
   /* paired with unref in grpc_tcp_destroy */
   gpr_ref_init(&tcp->refcount, 1);
   tcp->em_fd = em_fd;
diff --git a/src/core/lib/iomgr/tcp_server.h b/src/core/lib/iomgr/tcp_server.h
index 99b9f29..924121f 100644
--- a/src/core/lib/iomgr/tcp_server.h
+++ b/src/core/lib/iomgr/tcp_server.h
@@ -57,7 +57,8 @@
 /* Create a server, initially not bound to any ports. The caller owns one ref.
    If shutdown_complete is not NULL, it will be used by
    grpc_tcp_server_unref() when the ref count reaches zero. */
-grpc_tcp_server *grpc_tcp_server_create(grpc_closure *shutdown_complete);
+grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
+                                   grpc_tcp_server **server);
 
 /* Start listening to bound ports */
 void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server,
@@ -73,8 +74,8 @@
    but not dualstack sockets. */
 /* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle
                   all of the multiple socket port matching logic in one place */
-int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
-                             size_t addr_len);
+grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
+                                     size_t addr_len, int *out_port);
 
 /* Number of fds at the given port_index, or 0 if port_index is out of
    bounds. */
diff --git a/src/core/lib/iomgr/tcp_server_posix.c b/src/core/lib/iomgr/tcp_server_posix.c
index aaeb384..db70fad 100644
--- a/src/core/lib/iomgr/tcp_server_posix.c
+++ b/src/core/lib/iomgr/tcp_server_posix.c
@@ -59,6 +59,8 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
 #include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+
 #include "src/core/lib/iomgr/resolve_address.h"
 #include "src/core/lib/iomgr/sockaddr_utils.h"
 #include "src/core/lib/iomgr/socket_utils_posix.h"
@@ -130,7 +132,8 @@
   size_t pollset_count;
 };
 
-grpc_tcp_server *grpc_tcp_server_create(grpc_closure *shutdown_complete) {
+grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
+                                   grpc_tcp_server **server) {
   grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server));
   gpr_ref_init(&s->refs, 1);
   gpr_mu_init(&s->mu);
@@ -145,12 +148,13 @@
   s->head = NULL;
   s->tail = NULL;
   s->nports = 0;
-  return s;
+  *server = s;
+  return GRPC_ERROR_NONE;
 }
 
 static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
   if (s->shutdown_complete != NULL) {
-    grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL);
   }
 
   gpr_mu_destroy(&s->mu);
@@ -165,7 +169,7 @@
 }
 
 static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server,
-                           bool success) {
+                           grpc_error *error) {
   grpc_tcp_server *s = server;
   gpr_mu_lock(&s->mu);
   s->destroyed_ports++;
@@ -259,61 +263,68 @@
 }
 
 /* Prepare a recently-created socket for listening. */
-static int prepare_socket(int fd, const struct sockaddr *addr,
-                          size_t addr_len) {
+static grpc_error *prepare_socket(int fd, const struct sockaddr *addr,
+                                  size_t addr_len, int *port) {
   struct sockaddr_storage sockname_temp;
   socklen_t sockname_len;
+  grpc_error *err = GRPC_ERROR_NONE;
 
-  if (fd < 0) {
-    goto error;
-  }
+  GPR_ASSERT(fd >= 0);
 
-  if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) ||
-      (!grpc_is_unix_socket(addr) && (!grpc_set_socket_low_latency(fd, 1) ||
-                                      !grpc_set_socket_reuse_addr(fd, 1))) ||
-      !grpc_set_socket_no_sigpipe_if_possible(fd)) {
-    gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd,
-            strerror(errno));
-    goto error;
+  err = grpc_set_socket_nonblocking(fd, 1);
+  if (err != GRPC_ERROR_NONE) goto error;
+  err = grpc_set_socket_cloexec(fd, 1);
+  if (err != GRPC_ERROR_NONE) goto error;
+  if (!grpc_is_unix_socket(addr)) {
+    err = grpc_set_socket_low_latency(fd, 1);
+    if (err != GRPC_ERROR_NONE) goto error;
+    err = grpc_set_socket_reuse_addr(fd, 1);
+    if (err != GRPC_ERROR_NONE) goto error;
   }
+  err = grpc_set_socket_no_sigpipe_if_possible(fd);
+  if (err != GRPC_ERROR_NONE) goto error;
 
   GPR_ASSERT(addr_len < ~(socklen_t)0);
   if (bind(fd, addr, (socklen_t)addr_len) < 0) {
-    char *addr_str;
-    grpc_sockaddr_to_string(&addr_str, addr, 0);
-    gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno));
-    gpr_free(addr_str);
+    err = GRPC_OS_ERROR(errno, "bind");
     goto error;
   }
 
   if (listen(fd, get_max_accept_queue_size()) < 0) {
-    gpr_log(GPR_ERROR, "listen: %s", strerror(errno));
+    err = GRPC_OS_ERROR(errno, "listen");
     goto error;
   }
 
   sockname_len = sizeof(sockname_temp);
   if (getsockname(fd, (struct sockaddr *)&sockname_temp, &sockname_len) < 0) {
+    err = GRPC_OS_ERROR(errno, "getsockname");
     goto error;
   }
 
-  return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
+  *port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
+  return GRPC_ERROR_NONE;
 
 error:
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
   if (fd >= 0) {
     close(fd);
   }
-  return -1;
+  grpc_error *ret = grpc_error_set_int(
+      GRPC_ERROR_CREATE_REFERENCING("Unable to configure socket", &err, 1),
+      GRPC_ERROR_INT_FD, fd);
+  GRPC_ERROR_UNREF(err);
+  return ret;
 }
 
 /* event manager callback when reads are ready */
-static void on_read(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *err) {
   grpc_tcp_listener *sp = arg;
   grpc_tcp_server_acceptor acceptor = {sp->server, sp->port_index,
                                        sp->fd_index};
   grpc_fd *fdobj;
   size_t i;
 
-  if (!success) {
+  if (err != GRPC_ERROR_NONE) {
     goto error;
   }
 
@@ -376,18 +387,19 @@
   }
 }
 
-static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, int fd,
-                                               const struct sockaddr *addr,
-                                               size_t addr_len,
-                                               unsigned port_index,
-                                               unsigned fd_index) {
+static grpc_error *add_socket_to_server(grpc_tcp_server *s, int fd,
+                                        const struct sockaddr *addr,
+                                        size_t addr_len, unsigned port_index,
+                                        unsigned fd_index,
+                                        grpc_tcp_listener **listener) {
   grpc_tcp_listener *sp = NULL;
-  int port;
+  int port = -1;
   char *addr_str;
   char *name;
 
-  port = prepare_socket(fd, addr, addr_len);
-  if (port >= 0) {
+  grpc_error *err = prepare_socket(fd, addr, addr_len, &port);
+  if (err == GRPC_ERROR_NONE) {
+    GPR_ASSERT(port > 0);
     grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1);
     gpr_asprintf(&name, "tcp-server-listener:%s", addr_str);
     gpr_mu_lock(&s->mu);
@@ -417,11 +429,12 @@
     gpr_free(name);
   }
 
-  return sp;
+  *listener = sp;
+  return err;
 }
 
-int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
-                             size_t addr_len) {
+grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
+                                     size_t addr_len, int *out_port) {
   grpc_tcp_listener *sp;
   grpc_tcp_listener *sp2 = NULL;
   int fd;
@@ -436,6 +449,7 @@
   int port;
   unsigned port_index = 0;
   unsigned fd_index = 0;
+  grpc_error *errs[2] = {GRPC_ERROR_NONE, GRPC_ERROR_NONE};
   if (s->tail != NULL) {
     port_index = s->tail->port_index + 1;
   }
@@ -474,33 +488,35 @@
     /* Try listening on IPv6 first. */
     addr = (struct sockaddr *)&wild6;
     addr_len = sizeof(wild6);
-    fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
-    sp = add_socket_to_server(s, fd, addr, addr_len, port_index, fd_index);
-    if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) {
-      goto done;
+    errs[0] = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd);
+    if (errs[0] == GRPC_ERROR_NONE) {
+      errs[0] = add_socket_to_server(s, fd, addr, addr_len, port_index,
+                                     fd_index, &sp);
+      if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) {
+        goto done;
+      }
+      if (sp != NULL) {
+        ++fd_index;
+      }
+      /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */
+      if (port == 0 && sp != NULL) {
+        grpc_sockaddr_set_port((struct sockaddr *)&wild4, sp->port);
+      }
+      addr = (struct sockaddr *)&wild4;
+      addr_len = sizeof(wild4);
     }
-    if (sp != NULL) {
-      ++fd_index;
-    }
-    /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */
-    if (port == 0 && sp != NULL) {
-      grpc_sockaddr_set_port((struct sockaddr *)&wild4, sp->port);
-    }
-    addr = (struct sockaddr *)&wild4;
-    addr_len = sizeof(wild4);
   }
 
-  fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
-  if (fd < 0) {
-    gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
-  } else {
+  errs[1] = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd);
+  if (errs[1] == GRPC_ERROR_NONE) {
     if (dsmode == GRPC_DSMODE_IPV4 &&
         grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) {
       addr = (struct sockaddr *)&addr4_copy;
       addr_len = sizeof(addr4_copy);
     }
     sp2 = sp;
-    sp = add_socket_to_server(s, fd, addr, addr_len, port_index, fd_index);
+    errs[1] =
+        add_socket_to_server(s, fd, addr, addr_len, port_index, fd_index, &sp);
     if (sp2 != NULL && sp != NULL) {
       sp2->sibling = sp;
       sp->is_sibling = 1;
@@ -510,9 +526,21 @@
 done:
   gpr_free(allocated_addr);
   if (sp != NULL) {
-    return sp->port;
+    *out_port = sp->port;
+    GRPC_ERROR_UNREF(errs[0]);
+    GRPC_ERROR_UNREF(errs[1]);
+    return GRPC_ERROR_NONE;
   } else {
-    return -1;
+    *out_port = -1;
+    char *addr_str = grpc_sockaddr_to_uri(addr);
+    grpc_error *err = grpc_error_set_str(
+        GRPC_ERROR_CREATE_REFERENCING("Failed to add port to server", errs,
+                                      GPR_ARRAY_SIZE(errs)),
+        GRPC_ERROR_STR_TARGET_ADDRESS, addr_str);
+    GRPC_ERROR_UNREF(errs[0]);
+    GRPC_ERROR_UNREF(errs[1]);
+    gpr_free(addr_str);
+    return err;
   }
 }
 
@@ -581,7 +609,8 @@
 void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s,
                                            grpc_closure *shutdown_starting) {
   gpr_mu_lock(&s->mu);
-  grpc_closure_list_add(&s->shutdown_starting, shutdown_starting, 1);
+  grpc_closure_list_append(&s->shutdown_starting, shutdown_starting,
+                           GRPC_ERROR_NONE);
   gpr_mu_unlock(&s->mu);
 }
 
diff --git a/src/core/lib/iomgr/tcp_server_windows.c b/src/core/lib/iomgr/tcp_server_windows.c
index 125f521..cb7240e 100644
--- a/src/core/lib/iomgr/tcp_server_windows.c
+++ b/src/core/lib/iomgr/tcp_server_windows.c
@@ -102,7 +102,8 @@
 
 /* Public function. Allocates the proper data structures to hold a
    grpc_tcp_server. */
-grpc_tcp_server *grpc_tcp_server_create(grpc_closure *shutdown_complete) {
+grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete,
+                                   grpc_tcp_server **server) {
   grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server));
   gpr_ref_init(&s->refs, 1);
   gpr_mu_init(&s->mu);
@@ -114,12 +115,13 @@
   s->shutdown_starting.head = NULL;
   s->shutdown_starting.tail = NULL;
   s->shutdown_complete = shutdown_complete;
-  return s;
+  *server = s;
+  return GRPC_ERROR_NONE;
 }
 
 static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
   if (s->shutdown_complete != NULL) {
-    grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL);
   }
 
   /* Now that the accepts have been aborted, we can destroy the sockets.
@@ -143,7 +145,8 @@
 void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s,
                                            grpc_closure *shutdown_starting) {
   gpr_mu_lock(&s->mu);
-  grpc_closure_list_add(&s->shutdown_starting, shutdown_starting, 1);
+  grpc_closure_list_append(&s->shutdown_starting, shutdown_starting,
+                           GRPC_ERROR_NONE);
   gpr_mu_unlock(&s->mu);
 }
 
@@ -187,51 +190,49 @@
 }
 
 /* Prepare (bind) a recently-created socket for listening. */
-static int prepare_socket(SOCKET sock, const struct sockaddr *addr,
-                          size_t addr_len) {
+static grpc_error *prepare_socket(SOCKET sock, const struct sockaddr *addr,
+                                  size_t addr_len, int *port) {
   struct sockaddr_storage sockname_temp;
   socklen_t sockname_len;
+  grpc_error *error = GRPC_ERROR_NONE;
 
-  if (sock == INVALID_SOCKET) goto error;
-
-  if (!grpc_tcp_prepare_socket(sock)) {
-    char *utf8_message = gpr_format_message(WSAGetLastError());
-    gpr_log(GPR_ERROR, "Unable to prepare socket: %s", utf8_message);
-    gpr_free(utf8_message);
-    goto error;
+  error = grpc_tcp_prepare_socket(sock);
+  if (error != GRPC_ERROR_NONE) {
+    goto failure;
   }
 
   if (bind(sock, addr, (int)addr_len) == SOCKET_ERROR) {
-    char *addr_str;
-    char *utf8_message = gpr_format_message(WSAGetLastError());
-    grpc_sockaddr_to_string(&addr_str, addr, 0);
-    gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, utf8_message);
-    gpr_free(utf8_message);
-    gpr_free(addr_str);
-    goto error;
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "bind");
+    goto failure;
   }
 
   if (listen(sock, SOMAXCONN) == SOCKET_ERROR) {
-    char *utf8_message = gpr_format_message(WSAGetLastError());
-    gpr_log(GPR_ERROR, "listen: %s", utf8_message);
-    gpr_free(utf8_message);
-    goto error;
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "listen");
+    goto failure;
   }
 
   sockname_len = sizeof(sockname_temp);
   if (getsockname(sock, (struct sockaddr *)&sockname_temp, &sockname_len) ==
       SOCKET_ERROR) {
-    char *utf8_message = gpr_format_message(WSAGetLastError());
-    gpr_log(GPR_ERROR, "getsockname: %s", utf8_message);
-    gpr_free(utf8_message);
-    goto error;
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "getsockname");
+    goto failure;
   }
 
-  return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
+  *port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
+  return GRPC_ERROR_NONE;
 
-error:
+failure:
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  char *tgtaddr = grpc_sockaddr_to_uri(addr);
+  grpc_error *final_error = grpc_error_set_int(
+      grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING(
+                             "Failed to prepare server socket", &error, 1),
+                         GRPC_ERROR_STR_TARGET_ADDRESS, tgtaddr),
+      GRPC_ERROR_INT_FD, (intptr_t)sock);
+  gpr_free(tgtaddr);
+  GRPC_ERROR_UNREF(error);
   if (sock != INVALID_SOCKET) closesocket(sock);
-  return -1;
+  return error;
 }
 
 static void decrement_active_ports_and_notify(grpc_exec_ctx *exec_ctx,
@@ -251,26 +252,23 @@
 
 /* In order to do an async accept, we need to create a socket first which
    will be the one assigned to the new incoming connection. */
-static void start_accept(grpc_exec_ctx *exec_ctx, grpc_tcp_listener *port) {
+static grpc_error *start_accept(grpc_exec_ctx *exec_ctx,
+                                grpc_tcp_listener *port) {
   SOCKET sock = INVALID_SOCKET;
-  char *message;
-  char *utf8_message;
   BOOL success;
   DWORD addrlen = sizeof(struct sockaddr_in6) + 16;
   DWORD bytes_received = 0;
+  grpc_error *error = GRPC_ERROR_NONE;
 
   sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
                    WSA_FLAG_OVERLAPPED);
-
   if (sock == INVALID_SOCKET) {
-    message = "Unable to create socket: %s";
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
     goto failure;
   }
 
-  if (!grpc_tcp_prepare_socket(sock)) {
-    message = "Unable to prepare socket: %s";
-    goto failure;
-  }
+  error = grpc_tcp_prepare_socket(sock);
+  if (error != GRPC_ERROR_NONE) goto failure;
 
   /* Start the "accept" asynchronously. */
   success = port->AcceptEx(port->socket->socket, sock, port->addresses, 0,
@@ -280,9 +278,9 @@
   /* It is possible to get an accept immediately without delay. However, we
      will still get an IOCP notification for it. So let's just ignore it. */
   if (!success) {
-    int error = WSAGetLastError();
-    if (error != ERROR_IO_PENDING) {
-      message = "AcceptEx failed: %s";
+    int last_error = WSAGetLastError();
+    if (last_error != ERROR_IO_PENDING) {
+      error = GRPC_WSA_ERROR(last_error, "AcceptEx");
       goto failure;
     }
   }
@@ -291,9 +289,10 @@
      immediately process an accept that happened in the meantime. */
   port->new_socket = sock;
   grpc_socket_notify_on_read(exec_ctx, port->socket, &port->on_accept);
-  return;
+  return error;
 
 failure:
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
   if (port->shutting_down) {
     /* We are abandoning the listener port, take that into account to prevent
        occasional hangs on shutdown. The hang happens when sp->shutting_down
@@ -301,16 +300,15 @@
        but we fail there because the listening port has been closed in the
        meantime. */
     decrement_active_ports_and_notify(exec_ctx, port);
-    return;
+    GRPC_ERROR_UNREF(error);
+    return GRPC_ERROR_NONE;
   }
-  utf8_message = gpr_format_message(WSAGetLastError());
-  gpr_log(GPR_ERROR, message, utf8_message);
-  gpr_free(utf8_message);
   if (sock != INVALID_SOCKET) closesocket(sock);
+  return error;
 }
 
 /* Event manager callback when reads are ready. */
-static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, bool from_iocp) {
+static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   grpc_tcp_listener *sp = arg;
   grpc_tcp_server_acceptor acceptor = {sp->server, sp->port_index, 0};
   SOCKET sock = sp->new_socket;
@@ -328,7 +326,10 @@
   /* The general mechanism for shutting down is to queue abortion calls. While
      this is necessary in the read/write case, it's useless for the accept
      case. We only need to adjust the pending callback count */
-  if (!from_iocp) {
+  if (error != GRPC_ERROR_NONE) {
+    const char *msg = grpc_error_string(error);
+    gpr_log(GPR_INFO, "Skipping on_accept due to error: %s", msg);
+    grpc_error_free_string(msg);
     return;
   }
 
@@ -386,21 +387,20 @@
      the former socked we created has now either been destroy or assigned
      to the new connection. We need to create a new one for the next
      connection. */
-  start_accept(exec_ctx, sp);
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("start_accept", start_accept(exec_ctx, sp)));
 }
 
-static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
-                                               const struct sockaddr *addr,
-                                               size_t addr_len,
-                                               unsigned port_index) {
+static grpc_error *add_socket_to_server(grpc_tcp_server *s, SOCKET sock,
+                                        const struct sockaddr *addr,
+                                        size_t addr_len, unsigned port_index,
+                                        grpc_tcp_listener **listener) {
   grpc_tcp_listener *sp = NULL;
   int port;
   int status;
   GUID guid = WSAID_ACCEPTEX;
   DWORD ioctl_num_bytes;
   LPFN_ACCEPTEX AcceptEx;
-
-  if (sock == INVALID_SOCKET) return NULL;
+  grpc_error *error = GRPC_ERROR_NONE;
 
   /* We need to grab the AcceptEx pointer for that port, as it may be
      interface-dependent. We'll cache it to avoid doing that again. */
@@ -416,35 +416,39 @@
     return NULL;
   }
 
-  port = prepare_socket(sock, addr, addr_len);
-  if (port >= 0) {
-    gpr_mu_lock(&s->mu);
-    GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
-    sp = gpr_malloc(sizeof(grpc_tcp_listener));
-    sp->next = NULL;
-    if (s->head == NULL) {
-      s->head = sp;
-    } else {
-      s->tail->next = sp;
-    }
-    s->tail = sp;
-    sp->server = s;
-    sp->socket = grpc_winsocket_create(sock, "listener");
-    sp->shutting_down = 0;
-    sp->AcceptEx = AcceptEx;
-    sp->new_socket = INVALID_SOCKET;
-    sp->port = port;
-    sp->port_index = port_index;
-    grpc_closure_init(&sp->on_accept, on_accept, sp);
-    GPR_ASSERT(sp->socket);
-    gpr_mu_unlock(&s->mu);
+  error = prepare_socket(sock, addr, addr_len, &port);
+  if (error != GRPC_ERROR_NONE) {
+    return error;
   }
 
-  return sp;
+  GPR_ASSERT(port >= 0);
+  gpr_mu_lock(&s->mu);
+  GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
+  sp = gpr_malloc(sizeof(grpc_tcp_listener));
+  sp->next = NULL;
+  if (s->head == NULL) {
+    s->head = sp;
+  } else {
+    s->tail->next = sp;
+  }
+  s->tail = sp;
+  sp->server = s;
+  sp->socket = grpc_winsocket_create(sock, "listener");
+  sp->shutting_down = 0;
+  sp->AcceptEx = AcceptEx;
+  sp->new_socket = INVALID_SOCKET;
+  sp->port = port;
+  sp->port_index = port_index;
+  grpc_closure_init(&sp->on_accept, on_accept, sp);
+  GPR_ASSERT(sp->socket);
+  gpr_mu_unlock(&s->mu);
+  *listener = sp;
+
+  return GRPC_ERROR_NONE;
 }
 
-int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
-                             size_t addr_len) {
+grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr,
+                                     size_t addr_len, int *port) {
   grpc_tcp_listener *sp;
   SOCKET sock;
   struct sockaddr_in6 addr6_v4mapped;
@@ -452,8 +456,9 @@
   struct sockaddr *allocated_addr = NULL;
   struct sockaddr_storage sockname_temp;
   socklen_t sockname_len;
-  int port;
   unsigned port_index = 0;
+  grpc_error *error = GRPC_ERROR_NONE;
+
   if (s->tail != NULL) {
     port_index = s->tail->port_index + 1;
   }
@@ -465,11 +470,11 @@
       sockname_len = sizeof(sockname_temp);
       if (0 == getsockname(sp->socket->socket,
                            (struct sockaddr *)&sockname_temp, &sockname_len)) {
-        port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
-        if (port > 0) {
+        *port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
+        if (*port > 0) {
           allocated_addr = gpr_malloc(addr_len);
           memcpy(allocated_addr, addr, addr_len);
-          grpc_sockaddr_set_port(allocated_addr, port);
+          grpc_sockaddr_set_port(allocated_addr, *port);
           addr = allocated_addr;
           break;
         }
@@ -483,8 +488,8 @@
   }
 
   /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
-  if (grpc_sockaddr_is_wildcard(addr, &port)) {
-    grpc_sockaddr_make_wildcard6(port, &wildcard);
+  if (grpc_sockaddr_is_wildcard(addr, port)) {
+    grpc_sockaddr_make_wildcard6(*port, &wildcard);
 
     addr = (struct sockaddr *)&wildcard;
     addr_len = sizeof(wildcard);
@@ -493,19 +498,22 @@
   sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
                    WSA_FLAG_OVERLAPPED);
   if (sock == INVALID_SOCKET) {
-    char *utf8_message = gpr_format_message(WSAGetLastError());
-    gpr_log(GPR_ERROR, "unable to create socket: %s", utf8_message);
-    gpr_free(utf8_message);
+    error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket");
+    goto done;
   }
 
-  sp = add_socket_to_server(s, sock, addr, addr_len, port_index);
+  error = add_socket_to_server(s, sock, addr, addr_len, port_index, &sp);
+
+done:
   gpr_free(allocated_addr);
 
-  if (sp) {
-    return sp->port;
-  } else {
-    return -1;
+  if (error != GRPC_ERROR_NONE) {
+    grpc_error *error_out = GRPC_ERROR_CREATE_REFERENCING(
+        "Failed to add port to server", &error, 1);
+    GRPC_ERROR_UNREF(error);
+    error = error_out;
   }
+  return error;
 }
 
 void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
@@ -520,7 +528,7 @@
   s->on_accept_cb = on_accept_cb;
   s->on_accept_cb_arg = on_accept_cb_arg;
   for (sp = s->head; sp; sp = sp->next) {
-    start_accept(exec_ctx, sp);
+    GPR_ASSERT(GRPC_LOG_IF_ERROR("start_accept", start_accept(exec_ctx, sp)));
     s->active_ports++;
   }
   gpr_mu_unlock(&s->mu);
diff --git a/src/core/lib/iomgr/tcp_windows.c b/src/core/lib/iomgr/tcp_windows.c
index 551149e..e9a105f 100644
--- a/src/core/lib/iomgr/tcp_windows.c
+++ b/src/core/lib/iomgr/tcp_windows.c
@@ -61,27 +61,34 @@
 #define GRPC_FIONBIO FIONBIO
 #endif
 
-static int set_non_block(SOCKET sock) {
+static grpc_error *set_non_block(SOCKET sock) {
   int status;
   uint32_t param = 1;
   DWORD ret;
   status = WSAIoctl(sock, GRPC_FIONBIO, &param, sizeof(param), NULL, 0, &ret,
                     NULL, NULL);
-  return status == 0;
+  return status == 0
+             ? GRPC_ERROR_NONE
+             : GRPC_WSA_ERROR(WSAGetLastError(), "WSAIoctl(GRPC_FIONBIO)");
 }
 
-static int set_dualstack(SOCKET sock) {
+static grpc_error *set_dualstack(SOCKET sock) {
   int status;
   unsigned long param = 0;
   status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&param,
                       sizeof(param));
-  return status == 0;
+  return status == 0
+             ? GRPC_ERROR_NONE
+             : GRPC_WSA_ERROR(WSAGetLastError(), "setsockopt(IPV6_V6ONLY)");
 }
 
-int grpc_tcp_prepare_socket(SOCKET sock) {
-  if (!set_non_block(sock)) return 0;
-  if (!set_dualstack(sock)) return 0;
-  return 1;
+grpc_error *grpc_tcp_prepare_socket(SOCKET sock) {
+  grpc_error *err;
+  err = set_non_block(sock);
+  if (err != GRPC_ERROR_NONE) return err;
+  err = set_dualstack(sock);
+  if (err != GRPC_ERROR_NONE) return err;
+  return GRPC_ERROR_NONE;
 }
 
 typedef struct grpc_tcp {
@@ -148,39 +155,36 @@
 #endif
 
 /* Asynchronous callback from the IOCP, or the background thread. */
-static void on_read(grpc_exec_ctx *exec_ctx, void *tcpp, bool success) {
+static void on_read(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) {
   grpc_tcp *tcp = tcpp;
   grpc_closure *cb = tcp->read_cb;
   grpc_winsocket *socket = tcp->socket;
   gpr_slice sub;
   grpc_winsocket_callback_info *info = &socket->read_info;
 
-  if (success) {
+  GRPC_ERROR_REF(error);
+
+  if (error == GRPC_ERROR_NONE) {
     if (info->wsa_error != 0 && !tcp->shutting_down) {
-      if (info->wsa_error != WSAECONNRESET) {
-        char *utf8_message = gpr_format_message(info->wsa_error);
-        gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message);
-        gpr_free(utf8_message);
-      }
-      success = 0;
+      char *utf8_message = gpr_format_message(info->wsa_error);
+      gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message);
+      error = GRPC_ERROR_CREATE(utf8_message);
+      gpr_free(utf8_message);
       gpr_slice_unref(tcp->read_slice);
     } else {
       if (info->bytes_transfered != 0 && !tcp->shutting_down) {
         sub = gpr_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered);
         gpr_slice_buffer_add(tcp->read_slices, sub);
-        success = 1;
       } else {
         gpr_slice_unref(tcp->read_slice);
-        success = 0;
+        error = GRPC_ERROR_CREATE("End of TCP stream");
       }
     }
   }
 
   tcp->read_cb = NULL;
   TCP_UNREF(tcp, "read");
-  if (cb) {
-    cb->cb(exec_ctx, cb->cb_arg, success);
-  }
+  grpc_exec_ctx_push(exec_ctx, cb, error, NULL);
 }
 
 static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
@@ -194,7 +198,8 @@
   WSABUF buffer;
 
   if (tcp->shutting_down) {
-    grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, cb,
+                       GRPC_ERROR_CREATE("TCP socket is shutting down"), NULL);
     return;
   }
 
@@ -218,7 +223,7 @@
   /* Did we get data immediately ? Yay. */
   if (info->wsa_error != WSAEWOULDBLOCK) {
     info->bytes_transfered = bytes_read;
-    grpc_exec_ctx_enqueue(exec_ctx, &tcp->on_read, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, &tcp->on_read, GRPC_ERROR_NONE, NULL);
     return;
   }
 
@@ -231,7 +236,8 @@
     int wsa_error = WSAGetLastError();
     if (wsa_error != WSA_IO_PENDING) {
       info->wsa_error = wsa_error;
-      grpc_exec_ctx_enqueue(exec_ctx, &tcp->on_read, false, NULL);
+      grpc_exec_ctx_push(exec_ctx, &tcp->on_read,
+                         GRPC_WSA_ERROR(info->wsa_error, "WSARecv"), NULL);
       return;
     }
   }
@@ -240,32 +246,29 @@
 }
 
 /* Asynchronous callback from the IOCP, or the background thread. */
-static void on_write(grpc_exec_ctx *exec_ctx, void *tcpp, bool success) {
+static void on_write(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) {
   grpc_tcp *tcp = (grpc_tcp *)tcpp;
   grpc_winsocket *handle = tcp->socket;
   grpc_winsocket_callback_info *info = &handle->write_info;
   grpc_closure *cb;
 
+  GRPC_ERROR_REF(error);
+
   gpr_mu_lock(&tcp->mu);
   cb = tcp->write_cb;
   tcp->write_cb = NULL;
   gpr_mu_unlock(&tcp->mu);
 
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     if (info->wsa_error != 0) {
-      if (info->wsa_error != WSAECONNRESET) {
-        char *utf8_message = gpr_format_message(info->wsa_error);
-        gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message);
-        gpr_free(utf8_message);
-      }
-      success = 0;
+      error = GRPC_WSA_ERROR(info->wsa_error, "WSASend");
     } else {
       GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length);
     }
   }
 
   TCP_UNREF(tcp, "write");
-  cb->cb(exec_ctx, cb->cb_arg, success);
+  grpc_exec_ctx_push(exec_ctx, cb, error, NULL);
 }
 
 /* Initiates a write. */
@@ -283,7 +286,8 @@
   size_t len;
 
   if (tcp->shutting_down) {
-    grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, cb,
+                       GRPC_ERROR_CREATE("TCP socket is shutting down"), NULL);
     return;
   }
 
@@ -311,19 +315,10 @@
      connection that has its send queue filled up. But if we don't, then we can
      avoid doing an async write operation at all. */
   if (info->wsa_error != WSAEWOULDBLOCK) {
-    bool ok = false;
-    if (status == 0) {
-      ok = true;
-      GPR_ASSERT(bytes_sent == tcp->write_slices->length);
-    } else {
-      if (info->wsa_error != WSAECONNRESET) {
-        char *utf8_message = gpr_format_message(info->wsa_error);
-        gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
-        gpr_free(utf8_message);
-      }
-    }
-    if (allocated) gpr_free(allocated);
-    grpc_exec_ctx_enqueue(exec_ctx, cb, ok, NULL);
+    grpc_error *error = status == 0
+                            ? GRPC_ERROR_NONE
+                            : GRPC_WSA_ERROR(info->wsa_error, "WSASend");
+    grpc_exec_ctx_push(exec_ctx, cb, error, NULL);
     return;
   }
 
@@ -340,7 +335,8 @@
     int wsa_error = WSAGetLastError();
     if (wsa_error != WSA_IO_PENDING) {
       TCP_UNREF(tcp, "write");
-      grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL);
+      grpc_exec_ctx_push(exec_ctx, cb, GRPC_WSA_ERROR(wsa_error, "WSASend"),
+                         NULL);
       return;
     }
   }
diff --git a/src/core/lib/iomgr/tcp_windows.h b/src/core/lib/iomgr/tcp_windows.h
index a2f58ed..86d7772 100644
--- a/src/core/lib/iomgr/tcp_windows.h
+++ b/src/core/lib/iomgr/tcp_windows.h
@@ -52,6 +52,6 @@
  */
 grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string);
 
-int grpc_tcp_prepare_socket(SOCKET sock);
+grpc_error *grpc_tcp_prepare_socket(SOCKET sock);
 
 #endif /* GRPC_CORE_LIB_IOMGR_TCP_WINDOWS_H */
diff --git a/src/core/lib/iomgr/timer.c b/src/core/lib/iomgr/timer.c
index acb5b26..b46c6df 100644
--- a/src/core/lib/iomgr/timer.c
+++ b/src/core/lib/iomgr/timer.c
@@ -73,7 +73,7 @@
 static bool g_initialized = false;
 
 static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now,
-                                   gpr_timespec *next, int success);
+                                   gpr_timespec *next, grpc_error *error);
 
 static gpr_timespec compute_min_deadline(shard_type *shard) {
   return grpc_timer_heap_is_empty(&shard->heap)
@@ -105,7 +105,8 @@
 
 void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) {
   int i;
-  run_some_expired_timers(exec_ctx, gpr_inf_future(g_clock_type), NULL, 0);
+  run_some_expired_timers(exec_ctx, gpr_inf_future(g_clock_type), NULL,
+                          GRPC_ERROR_CREATE("Timer list shutdown"));
   for (i = 0; i < NUM_SHARDS; i++) {
     shard_type *shard = &g_shards[i];
     gpr_mu_destroy(&shard->mu);
@@ -185,13 +186,16 @@
 
   if (!g_initialized) {
     timer->triggered = 1;
-    grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, false, NULL);
+    grpc_exec_ctx_push(
+        exec_ctx, &timer->closure,
+        GRPC_ERROR_CREATE("Attempt to create timer before initialization"),
+        NULL);
     return;
   }
 
   if (gpr_time_cmp(deadline, now) <= 0) {
     timer->triggered = 1;
-    grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, &timer->closure, GRPC_ERROR_NONE, NULL);
     return;
   }
 
@@ -235,10 +239,15 @@
 }
 
 void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) {
+  if (!g_initialized) {
+    /* must have already been cancelled, also the shard mutex is invalid */
+    return;
+  }
+
   shard_type *shard = &g_shards[shard_idx(timer)];
   gpr_mu_lock(&shard->mu);
   if (!timer->triggered) {
-    grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, &timer->closure, GRPC_ERROR_CANCELLED, NULL);
     timer->triggered = 1;
     if (timer->heap_index == INVALID_HEAP_INDEX) {
       list_remove(timer);
@@ -299,12 +308,12 @@
 /* REQUIRES: shard->mu unlocked */
 static size_t pop_timers(grpc_exec_ctx *exec_ctx, shard_type *shard,
                          gpr_timespec now, gpr_timespec *new_min_deadline,
-                         int success) {
+                         grpc_error *error) {
   size_t n = 0;
   grpc_timer *timer;
   gpr_mu_lock(&shard->mu);
   while ((timer = pop_one(shard, now))) {
-    grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, success, NULL);
+    grpc_exec_ctx_push(exec_ctx, &timer->closure, GRPC_ERROR_REF(error), NULL);
     n++;
   }
   *new_min_deadline = compute_min_deadline(shard);
@@ -313,7 +322,7 @@
 }
 
 static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now,
-                                   gpr_timespec *next, int success) {
+                                   gpr_timespec *next, grpc_error *error) {
   size_t n = 0;
 
   /* TODO(ctiller): verify that there are any timers (atomically) here */
@@ -327,8 +336,8 @@
       /* For efficiency, we pop as many available timers as we can from the
          shard.  This may violate perfect timer deadline ordering, but that
          shouldn't be a big deal because we don't make ordering guarantees. */
-      n += pop_timers(exec_ctx, g_shard_queue[0], now, &new_min_deadline,
-                      success);
+      n +=
+          pop_timers(exec_ctx, g_shard_queue[0], now, &new_min_deadline, error);
 
       /* An grpc_timer_init() on the shard could intervene here, adding a new
          timer that is earlier than new_min_deadline.  However,
@@ -359,6 +368,8 @@
         *next, gpr_time_add(now, gpr_time_from_millis(1, GPR_TIMESPAN)));
   }
 
+  GRPC_ERROR_UNREF(error);
+
   return (int)n;
 }
 
@@ -367,5 +378,7 @@
   GPR_ASSERT(now.clock_type == g_clock_type);
   return run_some_expired_timers(
       exec_ctx, now, next,
-      gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0);
+      gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0
+          ? GRPC_ERROR_NONE
+          : GRPC_ERROR_CREATE("Shutting down timer system"));
 }
diff --git a/src/core/lib/iomgr/unix_sockets_posix.c b/src/core/lib/iomgr/unix_sockets_posix.c
index 5767c85..0e7670e 100644
--- a/src/core/lib/iomgr/unix_sockets_posix.c
+++ b/src/core/lib/iomgr/unix_sockets_posix.c
@@ -47,17 +47,18 @@
   GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
 }
 
-grpc_resolved_addresses *grpc_resolve_unix_domain_address(const char *name) {
+grpc_error *grpc_resolve_unix_domain_address(const char *name,
+                                             grpc_resolved_addresses **addrs) {
   struct sockaddr_un *un;
 
-  grpc_resolved_addresses *addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
-  addrs->naddrs = 1;
-  addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address));
-  un = (struct sockaddr_un *)addrs->addrs->addr;
+  *addrs = gpr_malloc(sizeof(grpc_resolved_addresses));
+  (*addrs)->naddrs = 1;
+  (*addrs)->addrs = gpr_malloc(sizeof(grpc_resolved_address));
+  un = (struct sockaddr_un *)(*addrs)->addrs->addr;
   un->sun_family = AF_UNIX;
   strcpy(un->sun_path, name);
-  addrs->addrs->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1;
-  return addrs;
+  (*addrs)->addrs->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1;
+  return GRPC_ERROR_NONE;
 }
 
 int grpc_is_unix_socket(const struct sockaddr *addr) {
diff --git a/src/core/lib/iomgr/unix_sockets_posix.h b/src/core/lib/iomgr/unix_sockets_posix.h
index 6758c49..db0516d 100644
--- a/src/core/lib/iomgr/unix_sockets_posix.h
+++ b/src/core/lib/iomgr/unix_sockets_posix.h
@@ -43,7 +43,8 @@
 
 void grpc_create_socketpair_if_unix(int sv[2]);
 
-grpc_resolved_addresses *grpc_resolve_unix_domain_address(const char *name);
+grpc_error *grpc_resolve_unix_domain_address(
+    const char *name, grpc_resolved_addresses **addresses);
 
 int grpc_is_unix_socket(const struct sockaddr *addr);
 
diff --git a/src/core/lib/iomgr/unix_sockets_posix_noop.c b/src/core/lib/iomgr/unix_sockets_posix_noop.c
index d309527..56b47c3 100644
--- a/src/core/lib/iomgr/unix_sockets_posix_noop.c
+++ b/src/core/lib/iomgr/unix_sockets_posix_noop.c
@@ -44,8 +44,10 @@
   GPR_ASSERT(0);
 }
 
-grpc_resolved_addresses *grpc_resolve_unix_domain_address(const char *name) {
-  return NULL;
+grpc_error *grpc_resolve_unix_domain_address(
+    const char *name, grpc_resolved_addresses **addresses) {
+  *addresses = NULL;
+  return GRPC_ERROR_CREATE("Unix domain sockets are not supported on Windows");
 }
 
 int grpc_is_unix_socket(const struct sockaddr *addr) { return false; }
diff --git a/src/core/lib/iomgr/wakeup_fd_eventfd.c b/src/core/lib/iomgr/wakeup_fd_eventfd.c
index 8a772ad..3ece30a 100644
--- a/src/core/lib/iomgr/wakeup_fd_eventfd.c
+++ b/src/core/lib/iomgr/wakeup_fd_eventfd.c
@@ -44,29 +44,39 @@
 #include "src/core/lib/iomgr/wakeup_fd_posix.h"
 #include "src/core/lib/profiling/timers.h"
 
-static void eventfd_create(grpc_wakeup_fd* fd_info) {
+static grpc_error* eventfd_create(grpc_wakeup_fd* fd_info) {
   int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
-  /* TODO(klempner): Handle failure more gracefully */
-  GPR_ASSERT(efd >= 0);
+  if (efd < 0) {
+    return GRPC_OS_ERROR(errno, "eventfd");
+  }
   fd_info->read_fd = efd;
   fd_info->write_fd = -1;
+  return GRPC_ERROR_NONE;
 }
 
-static void eventfd_consume(grpc_wakeup_fd* fd_info) {
+static grpc_error* eventfd_consume(grpc_wakeup_fd* fd_info) {
   eventfd_t value;
   int err;
   do {
     err = eventfd_read(fd_info->read_fd, &value);
   } while (err < 0 && errno == EINTR);
+  if (err < 0) {
+    return GRPC_OS_ERROR(errno, "eventfd_read");
+  }
+  return GRPC_ERROR_NONE;
 }
 
-static void eventfd_wakeup(grpc_wakeup_fd* fd_info) {
+static grpc_error* eventfd_wakeup(grpc_wakeup_fd* fd_info) {
   int err;
   GPR_TIMER_BEGIN("eventfd_wakeup", 0);
   do {
     err = eventfd_write(fd_info->read_fd, 1);
   } while (err < 0 && errno == EINTR);
+  if (err < 0) {
+    return GRPC_OS_ERROR(errno, "eventfd_write");
+  }
   GPR_TIMER_END("eventfd_wakeup", 0);
+  return GRPC_ERROR_NONE;
 }
 
 static void eventfd_destroy(grpc_wakeup_fd* fd_info) {
diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.c b/src/core/lib/iomgr/wakeup_fd_pipe.c
index e9b9a01..4e5dbdc 100644
--- a/src/core/lib/iomgr/wakeup_fd_pipe.c
+++ b/src/core/lib/iomgr/wakeup_fd_pipe.c
@@ -45,7 +45,7 @@
 
 #include "src/core/lib/iomgr/socket_utils_posix.h"
 
-static void pipe_init(grpc_wakeup_fd* fd_info) {
+static grpc_error* pipe_init(grpc_wakeup_fd* fd_info) {
   int pipefd[2];
   /* TODO(klempner): Make this nonfatal */
   int r = pipe(pipefd);
@@ -53,36 +53,40 @@
     gpr_log(GPR_ERROR, "pipe creation failed (%d): %s", errno, strerror(errno));
     abort();
   }
-  GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[0], 1));
-  GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[1], 1));
+  grpc_error* err;
+  err = grpc_set_socket_nonblocking(pipefd[0], 1);
+  if (err != GRPC_ERROR_NONE) return err;
+  err = grpc_set_socket_nonblocking(pipefd[1], 1);
+  if (err != GRPC_ERROR_NONE) return err;
   fd_info->read_fd = pipefd[0];
   fd_info->write_fd = pipefd[1];
+  return GRPC_ERROR_NONE;
 }
 
-static void pipe_consume(grpc_wakeup_fd* fd_info) {
+static grpc_error* pipe_consume(grpc_wakeup_fd* fd_info) {
   char buf[128];
   ssize_t r;
 
   for (;;) {
     r = read(fd_info->read_fd, buf, sizeof(buf));
     if (r > 0) continue;
-    if (r == 0) return;
+    if (r == 0) return GRPC_ERROR_NONE;
     switch (errno) {
       case EAGAIN:
-        return;
+        return GRPC_ERROR_NONE;
       case EINTR:
         continue;
       default:
-        gpr_log(GPR_ERROR, "error reading pipe: %s", strerror(errno));
-        return;
+        return GRPC_OS_ERROR(errno, "read");
     }
   }
 }
 
-static void pipe_wakeup(grpc_wakeup_fd* fd_info) {
+static grpc_error* pipe_wakeup(grpc_wakeup_fd* fd_info) {
   char c = 0;
   while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR)
     ;
+  return GRPC_ERROR_NONE;
 }
 
 static void pipe_destroy(grpc_wakeup_fd* fd_info) {
diff --git a/src/core/lib/iomgr/wakeup_fd_posix.c b/src/core/lib/iomgr/wakeup_fd_posix.c
index 525369c..046208a 100644
--- a/src/core/lib/iomgr/wakeup_fd_posix.c
+++ b/src/core/lib/iomgr/wakeup_fd_posix.c
@@ -53,16 +53,16 @@
 
 void grpc_wakeup_fd_global_destroy(void) { wakeup_fd_vtable = NULL; }
 
-void grpc_wakeup_fd_init(grpc_wakeup_fd *fd_info) {
-  wakeup_fd_vtable->init(fd_info);
+grpc_error *grpc_wakeup_fd_init(grpc_wakeup_fd *fd_info) {
+  return wakeup_fd_vtable->init(fd_info);
 }
 
-void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd *fd_info) {
-  wakeup_fd_vtable->consume(fd_info);
+grpc_error *grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd *fd_info) {
+  return wakeup_fd_vtable->consume(fd_info);
 }
 
-void grpc_wakeup_fd_wakeup(grpc_wakeup_fd *fd_info) {
-  wakeup_fd_vtable->wakeup(fd_info);
+grpc_error *grpc_wakeup_fd_wakeup(grpc_wakeup_fd *fd_info) {
+  return wakeup_fd_vtable->wakeup(fd_info);
 }
 
 void grpc_wakeup_fd_destroy(grpc_wakeup_fd *fd_info) {
diff --git a/src/core/lib/iomgr/wakeup_fd_posix.h b/src/core/lib/iomgr/wakeup_fd_posix.h
index 6b069c1..e269f24 100644
--- a/src/core/lib/iomgr/wakeup_fd_posix.h
+++ b/src/core/lib/iomgr/wakeup_fd_posix.h
@@ -62,6 +62,8 @@
 #ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H
 #define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H
 
+#include "src/core/lib/iomgr/error.h"
+
 void grpc_wakeup_fd_global_init(void);
 void grpc_wakeup_fd_global_destroy(void);
 
@@ -72,9 +74,9 @@
 typedef struct grpc_wakeup_fd grpc_wakeup_fd;
 
 typedef struct grpc_wakeup_fd_vtable {
-  void (*init)(grpc_wakeup_fd* fd_info);
-  void (*consume)(grpc_wakeup_fd* fd_info);
-  void (*wakeup)(grpc_wakeup_fd* fd_info);
+  grpc_error* (*init)(grpc_wakeup_fd* fd_info);
+  grpc_error* (*consume)(grpc_wakeup_fd* fd_info);
+  grpc_error* (*wakeup)(grpc_wakeup_fd* fd_info);
   void (*destroy)(grpc_wakeup_fd* fd_info);
   /* Must be called before calling any other functions */
   int (*check_availability)(void);
@@ -89,9 +91,10 @@
 
 #define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd)
 
-void grpc_wakeup_fd_init(grpc_wakeup_fd* fd_info);
-void grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd* fd_info);
-void grpc_wakeup_fd_wakeup(grpc_wakeup_fd* fd_info);
+grpc_error* grpc_wakeup_fd_init(grpc_wakeup_fd* fd_info) GRPC_MUST_USE_RESULT;
+grpc_error* grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd* fd_info)
+    GRPC_MUST_USE_RESULT;
+grpc_error* grpc_wakeup_fd_wakeup(grpc_wakeup_fd* fd_info) GRPC_MUST_USE_RESULT;
 void grpc_wakeup_fd_destroy(grpc_wakeup_fd* fd_info);
 
 /* Defined in some specialized implementation's .c file, or by
diff --git a/src/core/lib/iomgr/workqueue.h b/src/core/lib/iomgr/workqueue.h
index 3e2b223..6e8b121 100644
--- a/src/core/lib/iomgr/workqueue.h
+++ b/src/core/lib/iomgr/workqueue.h
@@ -50,7 +50,8 @@
 /* grpc_workqueue is forward declared in exec_ctx.h */
 
 /** Create a work queue */
-grpc_workqueue *grpc_workqueue_create(grpc_exec_ctx *exec_ctx);
+grpc_error *grpc_workqueue_create(grpc_exec_ctx *exec_ctx,
+                                  grpc_workqueue **workqueue);
 
 void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue);
 
@@ -77,7 +78,7 @@
                                    grpc_pollset *pollset);
 
 /** Add a work item to a workqueue */
-void grpc_workqueue_push(grpc_workqueue *workqueue, grpc_closure *closure,
-                         int success);
+void grpc_workqueue_push(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
+                         grpc_closure *closure, grpc_error *error);
 
 #endif /* GRPC_CORE_LIB_IOMGR_WORKQUEUE_H */
diff --git a/src/core/lib/iomgr/workqueue_posix.c b/src/core/lib/iomgr/workqueue_posix.c
index 80e7a0b..297e8d3 100644
--- a/src/core/lib/iomgr/workqueue_posix.c
+++ b/src/core/lib/iomgr/workqueue_posix.c
@@ -45,22 +45,27 @@
 
 #include "src/core/lib/iomgr/ev_posix.h"
 
-static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, bool success);
+static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error);
 
-grpc_workqueue *grpc_workqueue_create(grpc_exec_ctx *exec_ctx) {
+grpc_error *grpc_workqueue_create(grpc_exec_ctx *exec_ctx,
+                                  grpc_workqueue **workqueue) {
   char name[32];
-  grpc_workqueue *workqueue = gpr_malloc(sizeof(grpc_workqueue));
-  gpr_ref_init(&workqueue->refs, 1);
-  gpr_mu_init(&workqueue->mu);
-  workqueue->closure_list.head = workqueue->closure_list.tail = NULL;
-  grpc_wakeup_fd_init(&workqueue->wakeup_fd);
-  sprintf(name, "workqueue:%p", (void *)workqueue);
-  workqueue->wakeup_read_fd =
-      grpc_fd_create(GRPC_WAKEUP_FD_GET_READ_FD(&workqueue->wakeup_fd), name);
-  grpc_closure_init(&workqueue->read_closure, on_readable, workqueue);
-  grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd,
-                         &workqueue->read_closure);
-  return workqueue;
+  *workqueue = gpr_malloc(sizeof(grpc_workqueue));
+  gpr_ref_init(&(*workqueue)->refs, 1);
+  gpr_mu_init(&(*workqueue)->mu);
+  (*workqueue)->closure_list.head = (*workqueue)->closure_list.tail = NULL;
+  grpc_error *err = grpc_wakeup_fd_init(&(*workqueue)->wakeup_fd);
+  if (err != GRPC_ERROR_NONE) {
+    gpr_free(*workqueue);
+    return err;
+  }
+  sprintf(name, "workqueue:%p", (void *)(*workqueue));
+  (*workqueue)->wakeup_read_fd = grpc_fd_create(
+      GRPC_WAKEUP_FD_GET_READ_FD(&(*workqueue)->wakeup_fd), name);
+  grpc_closure_init(&(*workqueue)->read_closure, on_readable, *workqueue);
+  grpc_fd_notify_on_read(exec_ctx, (*workqueue)->wakeup_read_fd,
+                         &(*workqueue)->read_closure);
+  return GRPC_ERROR_NONE;
 }
 
 static void workqueue_destroy(grpc_exec_ctx *exec_ctx,
@@ -103,17 +108,14 @@
 
 void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {
   gpr_mu_lock(&workqueue->mu);
-  if (grpc_closure_list_empty(workqueue->closure_list)) {
-    grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd);
-  }
   grpc_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
   gpr_mu_unlock(&workqueue->mu);
 }
 
-static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   grpc_workqueue *workqueue = arg;
 
-  if (!success) {
+  if (error != GRPC_ERROR_NONE) {
     gpr_mu_destroy(&workqueue->mu);
     /* HACK: let wakeup_fd code know that we stole the fd */
     workqueue->wakeup_fd.read_fd = 0;
@@ -123,20 +125,32 @@
   } else {
     gpr_mu_lock(&workqueue->mu);
     grpc_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
-    grpc_wakeup_fd_consume_wakeup(&workqueue->wakeup_fd);
+    error = grpc_wakeup_fd_consume_wakeup(&workqueue->wakeup_fd);
     gpr_mu_unlock(&workqueue->mu);
-    grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd,
-                           &workqueue->read_closure);
+    if (error == GRPC_ERROR_NONE) {
+      grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd,
+                             &workqueue->read_closure);
+    } else {
+      /* recurse to get error handling */
+      on_readable(exec_ctx, arg, error);
+    }
   }
 }
 
-void grpc_workqueue_push(grpc_workqueue *workqueue, grpc_closure *closure,
-                         int success) {
+void grpc_workqueue_push(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
+                         grpc_closure *closure, grpc_error *error) {
+  grpc_error *push_error = GRPC_ERROR_NONE;
   gpr_mu_lock(&workqueue->mu);
   if (grpc_closure_list_empty(workqueue->closure_list)) {
-    grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd);
+    push_error = grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd);
   }
-  grpc_closure_list_add(&workqueue->closure_list, closure, success);
+  grpc_closure_list_append(&workqueue->closure_list, closure, error);
+  if (push_error != GRPC_ERROR_NONE) {
+    const char *msg = grpc_error_string(push_error);
+    gpr_log(GPR_ERROR, "Failed to push to workqueue: %s", msg);
+    grpc_error_free_string(msg);
+    grpc_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
+  }
   gpr_mu_unlock(&workqueue->mu);
 }
 
diff --git a/src/core/lib/security/security_context.c b/src/core/lib/security/context/security_context.c
similarity index 99%
rename from src/core/lib/security/security_context.c
rename to src/core/lib/security/context/security_context.c
index 343e0b5..127b13e 100644
--- a/src/core/lib/security/security_context.c
+++ b/src/core/lib/security/context/security_context.c
@@ -33,7 +33,7 @@
 
 #include <string.h>
 
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/api_trace.h"
 #include "src/core/lib/surface/call.h"
diff --git a/src/core/lib/security/security_context.h b/src/core/lib/security/context/security_context.h
similarity index 94%
rename from src/core/lib/security/security_context.h
rename to src/core/lib/security/context/security_context.h
index 81161ec..ef0c06b 100644
--- a/src/core/lib/security/security_context.h
+++ b/src/core/lib/security/context/security_context.h
@@ -31,11 +31,11 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONTEXT_H
-#define GRPC_CORE_LIB_SECURITY_SECURITY_CONTEXT_H
+#ifndef GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H
+#define GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H
 
 #include "src/core/lib/iomgr/pollset.h"
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 
 /* --- grpc_auth_context ---
 
@@ -111,4 +111,4 @@
 grpc_auth_context *grpc_find_auth_context_in_args(
     const grpc_channel_args *args);
 
-#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONTEXT_H */
+#endif /* GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H */
diff --git a/src/core/lib/security/credentials.c b/src/core/lib/security/credentials.c
deleted file mode 100644
index fd5ad35..0000000
--- a/src/core/lib/security/credentials.c
+++ /dev/null
@@ -1,1296 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "src/core/lib/security/credentials.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/channel/http_client_filter.h"
-#include "src/core/lib/http/httpcli.h"
-#include "src/core/lib/http/parser.h"
-#include "src/core/lib/iomgr/executor.h"
-#include "src/core/lib/json/json.h"
-#include "src/core/lib/support/string.h"
-#include "src/core/lib/surface/api_trace.h"
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/string_util.h>
-#include <grpc/support/sync.h>
-#include <grpc/support/time.h>
-
-/* -- Common. -- */
-
-struct grpc_credentials_metadata_request {
-  grpc_call_credentials *creds;
-  grpc_credentials_metadata_cb cb;
-  void *user_data;
-};
-
-static grpc_credentials_metadata_request *
-grpc_credentials_metadata_request_create(grpc_call_credentials *creds,
-                                         grpc_credentials_metadata_cb cb,
-                                         void *user_data) {
-  grpc_credentials_metadata_request *r =
-      gpr_malloc(sizeof(grpc_credentials_metadata_request));
-  r->creds = grpc_call_credentials_ref(creds);
-  r->cb = cb;
-  r->user_data = user_data;
-  return r;
-}
-
-static void grpc_credentials_metadata_request_destroy(
-    grpc_credentials_metadata_request *r) {
-  grpc_call_credentials_unref(r->creds);
-  gpr_free(r);
-}
-
-grpc_channel_credentials *grpc_channel_credentials_ref(
-    grpc_channel_credentials *creds) {
-  if (creds == NULL) return NULL;
-  gpr_ref(&creds->refcount);
-  return creds;
-}
-
-void grpc_channel_credentials_unref(grpc_channel_credentials *creds) {
-  if (creds == NULL) return;
-  if (gpr_unref(&creds->refcount)) {
-    if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds);
-    gpr_free(creds);
-  }
-}
-
-void grpc_channel_credentials_release(grpc_channel_credentials *creds) {
-  GRPC_API_TRACE("grpc_channel_credentials_release(creds=%p)", 1, (creds));
-  grpc_channel_credentials_unref(creds);
-}
-
-grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds) {
-  if (creds == NULL) return NULL;
-  gpr_ref(&creds->refcount);
-  return creds;
-}
-
-void grpc_call_credentials_unref(grpc_call_credentials *creds) {
-  if (creds == NULL) return;
-  if (gpr_unref(&creds->refcount)) {
-    if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds);
-    gpr_free(creds);
-  }
-}
-
-void grpc_call_credentials_release(grpc_call_credentials *creds) {
-  GRPC_API_TRACE("grpc_call_credentials_release(creds=%p)", 1, (creds));
-  grpc_call_credentials_unref(creds);
-}
-
-void grpc_call_credentials_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset *pollset, grpc_auth_metadata_context context,
-    grpc_credentials_metadata_cb cb, void *user_data) {
-  if (creds == NULL || creds->vtable->get_request_metadata == NULL) {
-    if (cb != NULL) {
-      cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK);
-    }
-    return;
-  }
-  creds->vtable->get_request_metadata(exec_ctx, creds, pollset, context, cb,
-                                      user_data);
-}
-
-grpc_security_status grpc_channel_credentials_create_security_connector(
-    grpc_channel_credentials *channel_creds, const char *target,
-    const grpc_channel_args *args, grpc_channel_security_connector **sc,
-    grpc_channel_args **new_args) {
-  *new_args = NULL;
-  if (channel_creds == NULL) {
-    return GRPC_SECURITY_ERROR;
-  }
-  GPR_ASSERT(channel_creds->vtable->create_security_connector != NULL);
-  return channel_creds->vtable->create_security_connector(
-      channel_creds, NULL, target, args, sc, new_args);
-}
-
-grpc_server_credentials *grpc_server_credentials_ref(
-    grpc_server_credentials *creds) {
-  if (creds == NULL) return NULL;
-  gpr_ref(&creds->refcount);
-  return creds;
-}
-
-void grpc_server_credentials_unref(grpc_server_credentials *creds) {
-  if (creds == NULL) return;
-  if (gpr_unref(&creds->refcount)) {
-    if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds);
-    if (creds->processor.destroy != NULL && creds->processor.state != NULL) {
-      creds->processor.destroy(creds->processor.state);
-    }
-    gpr_free(creds);
-  }
-}
-
-void grpc_server_credentials_release(grpc_server_credentials *creds) {
-  GRPC_API_TRACE("grpc_server_credentials_release(creds=%p)", 1, (creds));
-  grpc_server_credentials_unref(creds);
-}
-
-grpc_security_status grpc_server_credentials_create_security_connector(
-    grpc_server_credentials *creds, grpc_server_security_connector **sc) {
-  if (creds == NULL || creds->vtable->create_security_connector == NULL) {
-    gpr_log(GPR_ERROR, "Server credentials cannot create security context.");
-    return GRPC_SECURITY_ERROR;
-  }
-  return creds->vtable->create_security_connector(creds, sc);
-}
-
-void grpc_server_credentials_set_auth_metadata_processor(
-    grpc_server_credentials *creds, grpc_auth_metadata_processor processor) {
-  GRPC_API_TRACE(
-      "grpc_server_credentials_set_auth_metadata_processor("
-      "creds=%p, "
-      "processor=grpc_auth_metadata_processor { process: %p, state: %p })",
-      3, (creds, (void *)(intptr_t)processor.process, processor.state));
-  if (creds == NULL) return;
-  if (creds->processor.destroy != NULL && creds->processor.state != NULL) {
-    creds->processor.destroy(creds->processor.state);
-  }
-  creds->processor = processor;
-}
-
-static void server_credentials_pointer_arg_destroy(void *p) {
-  grpc_server_credentials_unref(p);
-}
-
-static void *server_credentials_pointer_arg_copy(void *p) {
-  return grpc_server_credentials_ref(p);
-}
-
-static int server_credentials_pointer_cmp(void *a, void *b) {
-  return GPR_ICMP(a, b);
-}
-
-static const grpc_arg_pointer_vtable cred_ptr_vtable = {
-    server_credentials_pointer_arg_copy, server_credentials_pointer_arg_destroy,
-    server_credentials_pointer_cmp};
-
-grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials *p) {
-  grpc_arg arg;
-  memset(&arg, 0, sizeof(grpc_arg));
-  arg.type = GRPC_ARG_POINTER;
-  arg.key = GRPC_SERVER_CREDENTIALS_ARG;
-  arg.value.pointer.p = p;
-  arg.value.pointer.vtable = &cred_ptr_vtable;
-  return arg;
-}
-
-grpc_server_credentials *grpc_server_credentials_from_arg(const grpc_arg *arg) {
-  if (strcmp(arg->key, GRPC_SERVER_CREDENTIALS_ARG) != 0) return NULL;
-  if (arg->type != GRPC_ARG_POINTER) {
-    gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type,
-            GRPC_SERVER_CREDENTIALS_ARG);
-    return NULL;
-  }
-  return arg->value.pointer.p;
-}
-
-grpc_server_credentials *grpc_find_server_credentials_in_args(
-    const grpc_channel_args *args) {
-  size_t i;
-  if (args == NULL) return NULL;
-  for (i = 0; i < args->num_args; i++) {
-    grpc_server_credentials *p =
-        grpc_server_credentials_from_arg(&args->args[i]);
-    if (p != NULL) return p;
-  }
-  return NULL;
-}
-
-/* -- Ssl credentials. -- */
-
-static void ssl_destruct(grpc_channel_credentials *creds) {
-  grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
-  if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
-  if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
-  if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain);
-}
-
-static void ssl_server_destruct(grpc_server_credentials *creds) {
-  grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
-  size_t i;
-  for (i = 0; i < c->config.num_key_cert_pairs; i++) {
-    if (c->config.pem_private_keys[i] != NULL) {
-      gpr_free(c->config.pem_private_keys[i]);
-    }
-    if (c->config.pem_cert_chains[i] != NULL) {
-      gpr_free(c->config.pem_cert_chains[i]);
-    }
-  }
-  if (c->config.pem_private_keys != NULL) gpr_free(c->config.pem_private_keys);
-  if (c->config.pem_private_keys_sizes != NULL) {
-    gpr_free(c->config.pem_private_keys_sizes);
-  }
-  if (c->config.pem_cert_chains != NULL) gpr_free(c->config.pem_cert_chains);
-  if (c->config.pem_cert_chains_sizes != NULL) {
-    gpr_free(c->config.pem_cert_chains_sizes);
-  }
-  if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
-}
-
-static grpc_security_status ssl_create_security_connector(
-    grpc_channel_credentials *creds, grpc_call_credentials *call_creds,
-    const char *target, const grpc_channel_args *args,
-    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
-  grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
-  grpc_security_status status = GRPC_SECURITY_OK;
-  size_t i = 0;
-  const char *overridden_target_name = NULL;
-  grpc_arg new_arg;
-
-  for (i = 0; args && i < args->num_args; i++) {
-    grpc_arg *arg = &args->args[i];
-    if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 &&
-        arg->type == GRPC_ARG_STRING) {
-      overridden_target_name = arg->value.string;
-      break;
-    }
-  }
-  status = grpc_ssl_channel_security_connector_create(
-      call_creds, &c->config, target, overridden_target_name, sc);
-  if (status != GRPC_SECURITY_OK) {
-    return status;
-  }
-  new_arg.type = GRPC_ARG_STRING;
-  new_arg.key = GRPC_ARG_HTTP2_SCHEME;
-  new_arg.value.string = "https";
-  *new_args = grpc_channel_args_copy_and_add(args, &new_arg, 1);
-  return status;
-}
-
-static grpc_security_status ssl_server_create_security_connector(
-    grpc_server_credentials *creds, grpc_server_security_connector **sc) {
-  grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
-  return grpc_ssl_server_security_connector_create(&c->config, sc);
-}
-
-static grpc_channel_credentials_vtable ssl_vtable = {
-    ssl_destruct, ssl_create_security_connector};
-
-static grpc_server_credentials_vtable ssl_server_vtable = {
-    ssl_server_destruct, ssl_server_create_security_connector};
-
-static void ssl_copy_key_material(const char *input, unsigned char **output,
-                                  size_t *output_size) {
-  *output_size = strlen(input);
-  *output = gpr_malloc(*output_size);
-  memcpy(*output, input, *output_size);
-}
-
-static void ssl_build_config(const char *pem_root_certs,
-                             grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
-                             grpc_ssl_config *config) {
-  if (pem_root_certs != NULL) {
-    ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
-                          &config->pem_root_certs_size);
-  }
-  if (pem_key_cert_pair != NULL) {
-    GPR_ASSERT(pem_key_cert_pair->private_key != NULL);
-    GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL);
-    ssl_copy_key_material(pem_key_cert_pair->private_key,
-                          &config->pem_private_key,
-                          &config->pem_private_key_size);
-    ssl_copy_key_material(pem_key_cert_pair->cert_chain,
-                          &config->pem_cert_chain,
-                          &config->pem_cert_chain_size);
-  }
-}
-
-static void ssl_build_server_config(
-    const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
-    size_t num_key_cert_pairs,
-    grpc_ssl_client_certificate_request_type client_certificate_request,
-    grpc_ssl_server_config *config) {
-  size_t i;
-  config->client_certificate_request = client_certificate_request;
-  if (pem_root_certs != NULL) {
-    ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
-                          &config->pem_root_certs_size);
-  }
-  if (num_key_cert_pairs > 0) {
-    GPR_ASSERT(pem_key_cert_pairs != NULL);
-    config->pem_private_keys =
-        gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
-    config->pem_cert_chains =
-        gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
-    config->pem_private_keys_sizes =
-        gpr_malloc(num_key_cert_pairs * sizeof(size_t));
-    config->pem_cert_chains_sizes =
-        gpr_malloc(num_key_cert_pairs * sizeof(size_t));
-  }
-  config->num_key_cert_pairs = num_key_cert_pairs;
-  for (i = 0; i < num_key_cert_pairs; i++) {
-    GPR_ASSERT(pem_key_cert_pairs[i].private_key != NULL);
-    GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != NULL);
-    ssl_copy_key_material(pem_key_cert_pairs[i].private_key,
-                          &config->pem_private_keys[i],
-                          &config->pem_private_keys_sizes[i]);
-    ssl_copy_key_material(pem_key_cert_pairs[i].cert_chain,
-                          &config->pem_cert_chains[i],
-                          &config->pem_cert_chains_sizes[i]);
-  }
-}
-
-grpc_channel_credentials *grpc_ssl_credentials_create(
-    const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
-    void *reserved) {
-  grpc_ssl_credentials *c = gpr_malloc(sizeof(grpc_ssl_credentials));
-  GRPC_API_TRACE(
-      "grpc_ssl_credentials_create(pem_root_certs=%s, "
-      "pem_key_cert_pair=%p, "
-      "reserved=%p)",
-      3, (pem_root_certs, pem_key_cert_pair, reserved));
-  GPR_ASSERT(reserved == NULL);
-  memset(c, 0, sizeof(grpc_ssl_credentials));
-  c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL;
-  c->base.vtable = &ssl_vtable;
-  gpr_ref_init(&c->base.refcount, 1);
-  ssl_build_config(pem_root_certs, pem_key_cert_pair, &c->config);
-  return &c->base;
-}
-
-grpc_server_credentials *grpc_ssl_server_credentials_create(
-    const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
-    size_t num_key_cert_pairs, int force_client_auth, void *reserved) {
-  return grpc_ssl_server_credentials_create_ex(
-      pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs,
-      force_client_auth
-          ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
-          : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
-      reserved);
-}
-
-grpc_server_credentials *grpc_ssl_server_credentials_create_ex(
-    const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
-    size_t num_key_cert_pairs,
-    grpc_ssl_client_certificate_request_type client_certificate_request,
-    void *reserved) {
-  grpc_ssl_server_credentials *c =
-      gpr_malloc(sizeof(grpc_ssl_server_credentials));
-  GRPC_API_TRACE(
-      "grpc_ssl_server_credentials_create_ex("
-      "pem_root_certs=%s, pem_key_cert_pairs=%p, num_key_cert_pairs=%lu, "
-      "client_certificate_request=%d, reserved=%p)",
-      5, (pem_root_certs, pem_key_cert_pairs, (unsigned long)num_key_cert_pairs,
-          client_certificate_request, reserved));
-  GPR_ASSERT(reserved == NULL);
-  memset(c, 0, sizeof(grpc_ssl_server_credentials));
-  c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL;
-  gpr_ref_init(&c->base.refcount, 1);
-  c->base.vtable = &ssl_server_vtable;
-  ssl_build_server_config(pem_root_certs, pem_key_cert_pairs,
-                          num_key_cert_pairs, client_certificate_request,
-                          &c->config);
-  return &c->base;
-}
-
-/* -- Jwt credentials -- */
-
-static void jwt_reset_cache(grpc_service_account_jwt_access_credentials *c) {
-  if (c->cached.jwt_md != NULL) {
-    grpc_credentials_md_store_unref(c->cached.jwt_md);
-    c->cached.jwt_md = NULL;
-  }
-  if (c->cached.service_url != NULL) {
-    gpr_free(c->cached.service_url);
-    c->cached.service_url = NULL;
-  }
-  c->cached.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
-}
-
-static void jwt_destruct(grpc_call_credentials *creds) {
-  grpc_service_account_jwt_access_credentials *c =
-      (grpc_service_account_jwt_access_credentials *)creds;
-  grpc_auth_json_key_destruct(&c->key);
-  jwt_reset_cache(c);
-  gpr_mu_destroy(&c->cache_mu);
-}
-
-static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
-                                     grpc_call_credentials *creds,
-                                     grpc_pollset *pollset,
-                                     grpc_auth_metadata_context context,
-                                     grpc_credentials_metadata_cb cb,
-                                     void *user_data) {
-  grpc_service_account_jwt_access_credentials *c =
-      (grpc_service_account_jwt_access_credentials *)creds;
-  gpr_timespec refresh_threshold = gpr_time_from_seconds(
-      GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
-
-  /* See if we can return a cached jwt. */
-  grpc_credentials_md_store *jwt_md = NULL;
-  {
-    gpr_mu_lock(&c->cache_mu);
-    if (c->cached.service_url != NULL &&
-        strcmp(c->cached.service_url, context.service_url) == 0 &&
-        c->cached.jwt_md != NULL &&
-        (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration,
-                                   gpr_now(GPR_CLOCK_REALTIME)),
-                      refresh_threshold) > 0)) {
-      jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md);
-    }
-    gpr_mu_unlock(&c->cache_mu);
-  }
-
-  if (jwt_md == NULL) {
-    char *jwt = NULL;
-    /* Generate a new jwt. */
-    gpr_mu_lock(&c->cache_mu);
-    jwt_reset_cache(c);
-    jwt = grpc_jwt_encode_and_sign(&c->key, context.service_url,
-                                   c->jwt_lifetime, NULL);
-    if (jwt != NULL) {
-      char *md_value;
-      gpr_asprintf(&md_value, "Bearer %s", jwt);
-      gpr_free(jwt);
-      c->cached.jwt_expiration =
-          gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c->jwt_lifetime);
-      c->cached.service_url = gpr_strdup(context.service_url);
-      c->cached.jwt_md = grpc_credentials_md_store_create(1);
-      grpc_credentials_md_store_add_cstrings(
-          c->cached.jwt_md, GRPC_AUTHORIZATION_METADATA_KEY, md_value);
-      gpr_free(md_value);
-      jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md);
-    }
-    gpr_mu_unlock(&c->cache_mu);
-  }
-
-  if (jwt_md != NULL) {
-    cb(exec_ctx, user_data, jwt_md->entries, jwt_md->num_entries,
-       GRPC_CREDENTIALS_OK);
-    grpc_credentials_md_store_unref(jwt_md);
-  } else {
-    cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR);
-  }
-}
-
-static grpc_call_credentials_vtable jwt_vtable = {jwt_destruct,
-                                                  jwt_get_request_metadata};
-
-grpc_call_credentials *
-grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
-    grpc_auth_json_key key, gpr_timespec token_lifetime) {
-  grpc_service_account_jwt_access_credentials *c;
-  if (!grpc_auth_json_key_is_valid(&key)) {
-    gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
-    return NULL;
-  }
-  c = gpr_malloc(sizeof(grpc_service_account_jwt_access_credentials));
-  memset(c, 0, sizeof(grpc_service_account_jwt_access_credentials));
-  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_JWT;
-  gpr_ref_init(&c->base.refcount, 1);
-  c->base.vtable = &jwt_vtable;
-  c->key = key;
-  c->jwt_lifetime = token_lifetime;
-  gpr_mu_init(&c->cache_mu);
-  jwt_reset_cache(c);
-  return &c->base;
-}
-
-grpc_call_credentials *grpc_service_account_jwt_access_credentials_create(
-    const char *json_key, gpr_timespec token_lifetime, void *reserved) {
-  GRPC_API_TRACE(
-      "grpc_service_account_jwt_access_credentials_create("
-      "json_key=%s, "
-      "token_lifetime="
-      "gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
-      "reserved=%p)",
-      5,
-      (json_key, (long long)token_lifetime.tv_sec, (int)token_lifetime.tv_nsec,
-       (int)token_lifetime.clock_type, reserved));
-  GPR_ASSERT(reserved == NULL);
-  return grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
-      grpc_auth_json_key_create_from_string(json_key), token_lifetime);
-}
-
-/* -- Oauth2TokenFetcher credentials -- */
-
-static void oauth2_token_fetcher_destruct(grpc_call_credentials *creds) {
-  grpc_oauth2_token_fetcher_credentials *c =
-      (grpc_oauth2_token_fetcher_credentials *)creds;
-  grpc_credentials_md_store_unref(c->access_token_md);
-  gpr_mu_destroy(&c->mu);
-  grpc_httpcli_context_destroy(&c->httpcli_context);
-}
-
-grpc_credentials_status
-grpc_oauth2_token_fetcher_credentials_parse_server_response(
-    const grpc_http_response *response, grpc_credentials_md_store **token_md,
-    gpr_timespec *token_lifetime) {
-  char *null_terminated_body = NULL;
-  char *new_access_token = NULL;
-  grpc_credentials_status status = GRPC_CREDENTIALS_OK;
-  grpc_json *json = NULL;
-
-  if (response == NULL) {
-    gpr_log(GPR_ERROR, "Received NULL response.");
-    status = GRPC_CREDENTIALS_ERROR;
-    goto end;
-  }
-
-  if (response->body_length > 0) {
-    null_terminated_body = gpr_malloc(response->body_length + 1);
-    null_terminated_body[response->body_length] = '\0';
-    memcpy(null_terminated_body, response->body, response->body_length);
-  }
-
-  if (response->status != 200) {
-    gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
-            response->status,
-            null_terminated_body != NULL ? null_terminated_body : "");
-    status = GRPC_CREDENTIALS_ERROR;
-    goto end;
-  } else {
-    grpc_json *access_token = NULL;
-    grpc_json *token_type = NULL;
-    grpc_json *expires_in = NULL;
-    grpc_json *ptr;
-    json = grpc_json_parse_string(null_terminated_body);
-    if (json == NULL) {
-      gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
-      status = GRPC_CREDENTIALS_ERROR;
-      goto end;
-    }
-    if (json->type != GRPC_JSON_OBJECT) {
-      gpr_log(GPR_ERROR, "Response should be a JSON object");
-      status = GRPC_CREDENTIALS_ERROR;
-      goto end;
-    }
-    for (ptr = json->child; ptr; ptr = ptr->next) {
-      if (strcmp(ptr->key, "access_token") == 0) {
-        access_token = ptr;
-      } else if (strcmp(ptr->key, "token_type") == 0) {
-        token_type = ptr;
-      } else if (strcmp(ptr->key, "expires_in") == 0) {
-        expires_in = ptr;
-      }
-    }
-    if (access_token == NULL || access_token->type != GRPC_JSON_STRING) {
-      gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
-      status = GRPC_CREDENTIALS_ERROR;
-      goto end;
-    }
-    if (token_type == NULL || token_type->type != GRPC_JSON_STRING) {
-      gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
-      status = GRPC_CREDENTIALS_ERROR;
-      goto end;
-    }
-    if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) {
-      gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
-      status = GRPC_CREDENTIALS_ERROR;
-      goto end;
-    }
-    gpr_asprintf(&new_access_token, "%s %s", token_type->value,
-                 access_token->value);
-    token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
-    token_lifetime->tv_nsec = 0;
-    token_lifetime->clock_type = GPR_TIMESPAN;
-    if (*token_md != NULL) grpc_credentials_md_store_unref(*token_md);
-    *token_md = grpc_credentials_md_store_create(1);
-    grpc_credentials_md_store_add_cstrings(
-        *token_md, GRPC_AUTHORIZATION_METADATA_KEY, new_access_token);
-    status = GRPC_CREDENTIALS_OK;
-  }
-
-end:
-  if (status != GRPC_CREDENTIALS_OK && (*token_md != NULL)) {
-    grpc_credentials_md_store_unref(*token_md);
-    *token_md = NULL;
-  }
-  if (null_terminated_body != NULL) gpr_free(null_terminated_body);
-  if (new_access_token != NULL) gpr_free(new_access_token);
-  if (json != NULL) grpc_json_destroy(json);
-  return status;
-}
-
-static void on_oauth2_token_fetcher_http_response(
-    grpc_exec_ctx *exec_ctx, void *user_data,
-    const grpc_http_response *response) {
-  grpc_credentials_metadata_request *r =
-      (grpc_credentials_metadata_request *)user_data;
-  grpc_oauth2_token_fetcher_credentials *c =
-      (grpc_oauth2_token_fetcher_credentials *)r->creds;
-  gpr_timespec token_lifetime;
-  grpc_credentials_status status;
-
-  gpr_mu_lock(&c->mu);
-  status = grpc_oauth2_token_fetcher_credentials_parse_server_response(
-      response, &c->access_token_md, &token_lifetime);
-  if (status == GRPC_CREDENTIALS_OK) {
-    c->token_expiration =
-        gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime);
-    r->cb(exec_ctx, r->user_data, c->access_token_md->entries,
-          c->access_token_md->num_entries, status);
-  } else {
-    c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
-    r->cb(exec_ctx, r->user_data, NULL, 0, status);
-  }
-  gpr_mu_unlock(&c->mu);
-  grpc_credentials_metadata_request_destroy(r);
-}
-
-static void oauth2_token_fetcher_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset *pollset, grpc_auth_metadata_context context,
-    grpc_credentials_metadata_cb cb, void *user_data) {
-  grpc_oauth2_token_fetcher_credentials *c =
-      (grpc_oauth2_token_fetcher_credentials *)creds;
-  gpr_timespec refresh_threshold = gpr_time_from_seconds(
-      GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
-  grpc_credentials_md_store *cached_access_token_md = NULL;
-  {
-    gpr_mu_lock(&c->mu);
-    if (c->access_token_md != NULL &&
-        (gpr_time_cmp(
-             gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)),
-             refresh_threshold) > 0)) {
-      cached_access_token_md =
-          grpc_credentials_md_store_ref(c->access_token_md);
-    }
-    gpr_mu_unlock(&c->mu);
-  }
-  if (cached_access_token_md != NULL) {
-    cb(exec_ctx, user_data, cached_access_token_md->entries,
-       cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK);
-    grpc_credentials_md_store_unref(cached_access_token_md);
-  } else {
-    c->fetch_func(
-        exec_ctx,
-        grpc_credentials_metadata_request_create(creds, cb, user_data),
-        &c->httpcli_context, pollset, on_oauth2_token_fetcher_http_response,
-        gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), refresh_threshold));
-  }
-}
-
-static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c,
-                                      grpc_fetch_oauth2_func fetch_func) {
-  memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials));
-  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
-  gpr_ref_init(&c->base.refcount, 1);
-  gpr_mu_init(&c->mu);
-  c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
-  c->fetch_func = fetch_func;
-  grpc_httpcli_context_init(&c->httpcli_context);
-}
-
-/* -- GoogleComputeEngine credentials. -- */
-
-static grpc_call_credentials_vtable compute_engine_vtable = {
-    oauth2_token_fetcher_destruct, oauth2_token_fetcher_get_request_metadata};
-
-static void compute_engine_fetch_oauth2(
-    grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
-    grpc_httpcli_context *httpcli_context, grpc_pollset *pollset,
-    grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
-  grpc_http_header header = {"Metadata-Flavor", "Google"};
-  grpc_httpcli_request request;
-  memset(&request, 0, sizeof(grpc_httpcli_request));
-  request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST;
-  request.http.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
-  request.http.hdr_count = 1;
-  request.http.hdrs = &header;
-  grpc_httpcli_get(exec_ctx, httpcli_context, pollset, &request, deadline,
-                   response_cb, metadata_req);
-}
-
-grpc_call_credentials *grpc_google_compute_engine_credentials_create(
-    void *reserved) {
-  grpc_oauth2_token_fetcher_credentials *c =
-      gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials));
-  GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1,
-                 (reserved));
-  GPR_ASSERT(reserved == NULL);
-  init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2);
-  c->base.vtable = &compute_engine_vtable;
-  return &c->base;
-}
-
-/* -- GoogleRefreshToken credentials. -- */
-
-static void refresh_token_destruct(grpc_call_credentials *creds) {
-  grpc_google_refresh_token_credentials *c =
-      (grpc_google_refresh_token_credentials *)creds;
-  grpc_auth_refresh_token_destruct(&c->refresh_token);
-  oauth2_token_fetcher_destruct(&c->base.base);
-}
-
-static grpc_call_credentials_vtable refresh_token_vtable = {
-    refresh_token_destruct, oauth2_token_fetcher_get_request_metadata};
-
-static void refresh_token_fetch_oauth2(
-    grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
-    grpc_httpcli_context *httpcli_context, grpc_pollset *pollset,
-    grpc_httpcli_response_cb response_cb, gpr_timespec deadline) {
-  grpc_google_refresh_token_credentials *c =
-      (grpc_google_refresh_token_credentials *)metadata_req->creds;
-  grpc_http_header header = {"Content-Type",
-                             "application/x-www-form-urlencoded"};
-  grpc_httpcli_request request;
-  char *body = NULL;
-  gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING,
-               c->refresh_token.client_id, c->refresh_token.client_secret,
-               c->refresh_token.refresh_token);
-  memset(&request, 0, sizeof(grpc_httpcli_request));
-  request.host = GRPC_GOOGLE_OAUTH2_SERVICE_HOST;
-  request.http.path = GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
-  request.http.hdr_count = 1;
-  request.http.hdrs = &header;
-  request.handshaker = &grpc_httpcli_ssl;
-  grpc_httpcli_post(exec_ctx, httpcli_context, pollset, &request, body,
-                    strlen(body), deadline, response_cb, metadata_req);
-  gpr_free(body);
-}
-
-grpc_call_credentials *
-grpc_refresh_token_credentials_create_from_auth_refresh_token(
-    grpc_auth_refresh_token refresh_token) {
-  grpc_google_refresh_token_credentials *c;
-  if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
-    gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation");
-    return NULL;
-  }
-  c = gpr_malloc(sizeof(grpc_google_refresh_token_credentials));
-  memset(c, 0, sizeof(grpc_google_refresh_token_credentials));
-  init_oauth2_token_fetcher(&c->base, refresh_token_fetch_oauth2);
-  c->base.base.vtable = &refresh_token_vtable;
-  c->refresh_token = refresh_token;
-  return &c->base.base;
-}
-
-grpc_call_credentials *grpc_google_refresh_token_credentials_create(
-    const char *json_refresh_token, void *reserved) {
-  GRPC_API_TRACE(
-      "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
-      "reserved=%p)",
-      2, (json_refresh_token, reserved));
-  GPR_ASSERT(reserved == NULL);
-  return grpc_refresh_token_credentials_create_from_auth_refresh_token(
-      grpc_auth_refresh_token_create_from_string(json_refresh_token));
-}
-
-/* -- Metadata-only credentials. -- */
-
-static void md_only_test_destruct(grpc_call_credentials *creds) {
-  grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds;
-  grpc_credentials_md_store_unref(c->md_store);
-}
-
-static void on_simulated_token_fetch_done(grpc_exec_ctx *exec_ctx,
-                                          void *user_data, bool success) {
-  grpc_credentials_metadata_request *r =
-      (grpc_credentials_metadata_request *)user_data;
-  grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)r->creds;
-  r->cb(exec_ctx, r->user_data, c->md_store->entries, c->md_store->num_entries,
-        GRPC_CREDENTIALS_OK);
-  grpc_credentials_metadata_request_destroy(r);
-}
-
-static void md_only_test_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset *pollset, grpc_auth_metadata_context context,
-    grpc_credentials_metadata_cb cb, void *user_data) {
-  grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds;
-
-  if (c->is_async) {
-    grpc_credentials_metadata_request *cb_arg =
-        grpc_credentials_metadata_request_create(creds, cb, user_data);
-    grpc_executor_enqueue(
-        grpc_closure_create(on_simulated_token_fetch_done, cb_arg), true);
-  } else {
-    cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK);
-  }
-}
-
-static grpc_call_credentials_vtable md_only_test_vtable = {
-    md_only_test_destruct, md_only_test_get_request_metadata};
-
-grpc_call_credentials *grpc_md_only_test_credentials_create(
-    const char *md_key, const char *md_value, int is_async) {
-  grpc_md_only_test_credentials *c =
-      gpr_malloc(sizeof(grpc_md_only_test_credentials));
-  memset(c, 0, sizeof(grpc_md_only_test_credentials));
-  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
-  c->base.vtable = &md_only_test_vtable;
-  gpr_ref_init(&c->base.refcount, 1);
-  c->md_store = grpc_credentials_md_store_create(1);
-  grpc_credentials_md_store_add_cstrings(c->md_store, md_key, md_value);
-  c->is_async = is_async;
-  return &c->base;
-}
-
-/* -- Oauth2 Access Token credentials. -- */
-
-static void access_token_destruct(grpc_call_credentials *creds) {
-  grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
-  grpc_credentials_md_store_unref(c->access_token_md);
-}
-
-static void access_token_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset *pollset, grpc_auth_metadata_context context,
-    grpc_credentials_metadata_cb cb, void *user_data) {
-  grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
-  cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK);
-}
-
-static grpc_call_credentials_vtable access_token_vtable = {
-    access_token_destruct, access_token_get_request_metadata};
-
-grpc_call_credentials *grpc_access_token_credentials_create(
-    const char *access_token, void *reserved) {
-  grpc_access_token_credentials *c =
-      gpr_malloc(sizeof(grpc_access_token_credentials));
-  char *token_md_value;
-  GRPC_API_TRACE(
-      "grpc_access_token_credentials_create(access_token=%s, "
-      "reserved=%p)",
-      2, (access_token, reserved));
-  GPR_ASSERT(reserved == NULL);
-  memset(c, 0, sizeof(grpc_access_token_credentials));
-  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
-  c->base.vtable = &access_token_vtable;
-  gpr_ref_init(&c->base.refcount, 1);
-  c->access_token_md = grpc_credentials_md_store_create(1);
-  gpr_asprintf(&token_md_value, "Bearer %s", access_token);
-  grpc_credentials_md_store_add_cstrings(
-      c->access_token_md, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value);
-  gpr_free(token_md_value);
-  return &c->base;
-}
-
-/* -- Fake transport security credentials. -- */
-
-static grpc_security_status fake_transport_security_create_security_connector(
-    grpc_channel_credentials *c, grpc_call_credentials *call_creds,
-    const char *target, const grpc_channel_args *args,
-    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
-  *sc = grpc_fake_channel_security_connector_create(call_creds);
-  return GRPC_SECURITY_OK;
-}
-
-static grpc_security_status
-fake_transport_security_server_create_security_connector(
-    grpc_server_credentials *c, grpc_server_security_connector **sc) {
-  *sc = grpc_fake_server_security_connector_create();
-  return GRPC_SECURITY_OK;
-}
-
-static grpc_channel_credentials_vtable
-    fake_transport_security_credentials_vtable = {
-        NULL, fake_transport_security_create_security_connector};
-
-static grpc_server_credentials_vtable
-    fake_transport_security_server_credentials_vtable = {
-        NULL, fake_transport_security_server_create_security_connector};
-
-grpc_channel_credentials *grpc_fake_transport_security_credentials_create(
-    void) {
-  grpc_channel_credentials *c = gpr_malloc(sizeof(grpc_channel_credentials));
-  memset(c, 0, sizeof(grpc_channel_credentials));
-  c->type = GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
-  c->vtable = &fake_transport_security_credentials_vtable;
-  gpr_ref_init(&c->refcount, 1);
-  return c;
-}
-
-grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
-    void) {
-  grpc_server_credentials *c = gpr_malloc(sizeof(grpc_server_credentials));
-  memset(c, 0, sizeof(grpc_server_credentials));
-  c->type = GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
-  gpr_ref_init(&c->refcount, 1);
-  c->vtable = &fake_transport_security_server_credentials_vtable;
-  return c;
-}
-
-/* -- Composite call credentials. -- */
-
-typedef struct {
-  grpc_composite_call_credentials *composite_creds;
-  size_t creds_index;
-  grpc_credentials_md_store *md_elems;
-  grpc_auth_metadata_context auth_md_context;
-  void *user_data;
-  grpc_pollset *pollset;
-  grpc_credentials_metadata_cb cb;
-} grpc_composite_call_credentials_metadata_context;
-
-static void composite_call_destruct(grpc_call_credentials *creds) {
-  grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds;
-  size_t i;
-  for (i = 0; i < c->inner.num_creds; i++) {
-    grpc_call_credentials_unref(c->inner.creds_array[i]);
-  }
-  gpr_free(c->inner.creds_array);
-}
-
-static void composite_call_md_context_destroy(
-    grpc_composite_call_credentials_metadata_context *ctx) {
-  grpc_credentials_md_store_unref(ctx->md_elems);
-  gpr_free(ctx);
-}
-
-static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data,
-                                       grpc_credentials_md *md_elems,
-                                       size_t num_md,
-                                       grpc_credentials_status status) {
-  grpc_composite_call_credentials_metadata_context *ctx =
-      (grpc_composite_call_credentials_metadata_context *)user_data;
-  if (status != GRPC_CREDENTIALS_OK) {
-    ctx->cb(exec_ctx, ctx->user_data, NULL, 0, status);
-    return;
-  }
-
-  /* Copy the metadata in the context. */
-  if (num_md > 0) {
-    size_t i;
-    for (i = 0; i < num_md; i++) {
-      grpc_credentials_md_store_add(ctx->md_elems, md_elems[i].key,
-                                    md_elems[i].value);
-    }
-  }
-
-  /* See if we need to get some more metadata. */
-  if (ctx->creds_index < ctx->composite_creds->inner.num_creds) {
-    grpc_call_credentials *inner_creds =
-        ctx->composite_creds->inner.creds_array[ctx->creds_index++];
-    grpc_call_credentials_get_request_metadata(
-        exec_ctx, inner_creds, ctx->pollset, ctx->auth_md_context,
-        composite_call_metadata_cb, ctx);
-    return;
-  }
-
-  /* We're done!. */
-  ctx->cb(exec_ctx, ctx->user_data, ctx->md_elems->entries,
-          ctx->md_elems->num_entries, GRPC_CREDENTIALS_OK);
-  composite_call_md_context_destroy(ctx);
-}
-
-static void composite_call_get_request_metadata(
-    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
-    grpc_pollset *pollset, grpc_auth_metadata_context auth_md_context,
-    grpc_credentials_metadata_cb cb, void *user_data) {
-  grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds;
-  grpc_composite_call_credentials_metadata_context *ctx;
-
-  ctx = gpr_malloc(sizeof(grpc_composite_call_credentials_metadata_context));
-  memset(ctx, 0, sizeof(grpc_composite_call_credentials_metadata_context));
-  ctx->auth_md_context = auth_md_context;
-  ctx->user_data = user_data;
-  ctx->cb = cb;
-  ctx->composite_creds = c;
-  ctx->pollset = pollset;
-  ctx->md_elems = grpc_credentials_md_store_create(c->inner.num_creds);
-  grpc_call_credentials_get_request_metadata(
-      exec_ctx, c->inner.creds_array[ctx->creds_index++], pollset,
-      auth_md_context, composite_call_metadata_cb, ctx);
-}
-
-static grpc_call_credentials_vtable composite_call_credentials_vtable = {
-    composite_call_destruct, composite_call_get_request_metadata};
-
-static grpc_call_credentials_array get_creds_array(
-    grpc_call_credentials **creds_addr) {
-  grpc_call_credentials_array result;
-  grpc_call_credentials *creds = *creds_addr;
-  result.creds_array = creds_addr;
-  result.num_creds = 1;
-  if (strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0) {
-    result = *grpc_composite_call_credentials_get_credentials(creds);
-  }
-  return result;
-}
-
-grpc_call_credentials *grpc_composite_call_credentials_create(
-    grpc_call_credentials *creds1, grpc_call_credentials *creds2,
-    void *reserved) {
-  size_t i;
-  size_t creds_array_byte_size;
-  grpc_call_credentials_array creds1_array;
-  grpc_call_credentials_array creds2_array;
-  grpc_composite_call_credentials *c;
-  GRPC_API_TRACE(
-      "grpc_composite_call_credentials_create(creds1=%p, creds2=%p, "
-      "reserved=%p)",
-      3, (creds1, creds2, reserved));
-  GPR_ASSERT(reserved == NULL);
-  GPR_ASSERT(creds1 != NULL);
-  GPR_ASSERT(creds2 != NULL);
-  c = gpr_malloc(sizeof(grpc_composite_call_credentials));
-  memset(c, 0, sizeof(grpc_composite_call_credentials));
-  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE;
-  c->base.vtable = &composite_call_credentials_vtable;
-  gpr_ref_init(&c->base.refcount, 1);
-  creds1_array = get_creds_array(&creds1);
-  creds2_array = get_creds_array(&creds2);
-  c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds;
-  creds_array_byte_size = c->inner.num_creds * sizeof(grpc_call_credentials *);
-  c->inner.creds_array = gpr_malloc(creds_array_byte_size);
-  memset(c->inner.creds_array, 0, creds_array_byte_size);
-  for (i = 0; i < creds1_array.num_creds; i++) {
-    grpc_call_credentials *cur_creds = creds1_array.creds_array[i];
-    c->inner.creds_array[i] = grpc_call_credentials_ref(cur_creds);
-  }
-  for (i = 0; i < creds2_array.num_creds; i++) {
-    grpc_call_credentials *cur_creds = creds2_array.creds_array[i];
-    c->inner.creds_array[i + creds1_array.num_creds] =
-        grpc_call_credentials_ref(cur_creds);
-  }
-  return &c->base;
-}
-
-const grpc_call_credentials_array *
-grpc_composite_call_credentials_get_credentials(grpc_call_credentials *creds) {
-  const grpc_composite_call_credentials *c =
-      (const grpc_composite_call_credentials *)creds;
-  GPR_ASSERT(strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0);
-  return &c->inner;
-}
-
-grpc_call_credentials *grpc_credentials_contains_type(
-    grpc_call_credentials *creds, const char *type,
-    grpc_call_credentials **composite_creds) {
-  size_t i;
-  if (strcmp(creds->type, type) == 0) {
-    if (composite_creds != NULL) *composite_creds = NULL;
-    return creds;
-  } else if (strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0) {
-    const grpc_call_credentials_array *inner_creds_array =
-        grpc_composite_call_credentials_get_credentials(creds);
-    for (i = 0; i < inner_creds_array->num_creds; i++) {
-      if (strcmp(type, inner_creds_array->creds_array[i]->type) == 0) {
-        if (composite_creds != NULL) *composite_creds = creds;
-        return inner_creds_array->creds_array[i];
-      }
-    }
-  }
-  return NULL;
-}
-
-/* -- IAM credentials. -- */
-
-static void iam_destruct(grpc_call_credentials *creds) {
-  grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds;
-  grpc_credentials_md_store_unref(c->iam_md);
-}
-
-static void iam_get_request_metadata(grpc_exec_ctx *exec_ctx,
-                                     grpc_call_credentials *creds,
-                                     grpc_pollset *pollset,
-                                     grpc_auth_metadata_context context,
-                                     grpc_credentials_metadata_cb cb,
-                                     void *user_data) {
-  grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds;
-  cb(exec_ctx, user_data, c->iam_md->entries, c->iam_md->num_entries,
-     GRPC_CREDENTIALS_OK);
-}
-
-static grpc_call_credentials_vtable iam_vtable = {iam_destruct,
-                                                  iam_get_request_metadata};
-
-grpc_call_credentials *grpc_google_iam_credentials_create(
-    const char *token, const char *authority_selector, void *reserved) {
-  grpc_google_iam_credentials *c;
-  GRPC_API_TRACE(
-      "grpc_iam_credentials_create(token=%s, authority_selector=%s, "
-      "reserved=%p)",
-      3, (token, authority_selector, reserved));
-  GPR_ASSERT(reserved == NULL);
-  GPR_ASSERT(token != NULL);
-  GPR_ASSERT(authority_selector != NULL);
-  c = gpr_malloc(sizeof(grpc_google_iam_credentials));
-  memset(c, 0, sizeof(grpc_google_iam_credentials));
-  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_IAM;
-  c->base.vtable = &iam_vtable;
-  gpr_ref_init(&c->base.refcount, 1);
-  c->iam_md = grpc_credentials_md_store_create(2);
-  grpc_credentials_md_store_add_cstrings(
-      c->iam_md, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, token);
-  grpc_credentials_md_store_add_cstrings(
-      c->iam_md, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector);
-  return &c->base;
-}
-
-/* -- Plugin credentials. -- */
-
-typedef struct {
-  void *user_data;
-  grpc_credentials_metadata_cb cb;
-} grpc_metadata_plugin_request;
-
-static void plugin_destruct(grpc_call_credentials *creds) {
-  grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds;
-  if (c->plugin.state != NULL && c->plugin.destroy != NULL) {
-    c->plugin.destroy(c->plugin.state);
-  }
-}
-
-static void plugin_md_request_metadata_ready(void *request,
-                                             const grpc_metadata *md,
-                                             size_t num_md,
-                                             grpc_status_code status,
-                                             const char *error_details) {
-  /* called from application code */
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_metadata_plugin_request *r = (grpc_metadata_plugin_request *)request;
-  if (status != GRPC_STATUS_OK) {
-    if (error_details != NULL) {
-      gpr_log(GPR_ERROR, "Getting metadata from plugin failed with error: %s",
-              error_details);
-    }
-    r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR);
-  } else {
-    size_t i;
-    grpc_credentials_md *md_array = NULL;
-    if (num_md > 0) {
-      md_array = gpr_malloc(num_md * sizeof(grpc_credentials_md));
-      for (i = 0; i < num_md; i++) {
-        md_array[i].key = gpr_slice_from_copied_string(md[i].key);
-        md_array[i].value =
-            gpr_slice_from_copied_buffer(md[i].value, md[i].value_length);
-      }
-    }
-    r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK);
-    if (md_array != NULL) {
-      for (i = 0; i < num_md; i++) {
-        gpr_slice_unref(md_array[i].key);
-        gpr_slice_unref(md_array[i].value);
-      }
-      gpr_free(md_array);
-    }
-  }
-  gpr_free(r);
-  grpc_exec_ctx_finish(&exec_ctx);
-}
-
-static void plugin_get_request_metadata(grpc_exec_ctx *exec_ctx,
-                                        grpc_call_credentials *creds,
-                                        grpc_pollset *pollset,
-                                        grpc_auth_metadata_context context,
-                                        grpc_credentials_metadata_cb cb,
-                                        void *user_data) {
-  grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds;
-  if (c->plugin.get_metadata != NULL) {
-    grpc_metadata_plugin_request *request = gpr_malloc(sizeof(*request));
-    memset(request, 0, sizeof(*request));
-    request->user_data = user_data;
-    request->cb = cb;
-    c->plugin.get_metadata(c->plugin.state, context,
-                           plugin_md_request_metadata_ready, request);
-  } else {
-    cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK);
-  }
-}
-
-static grpc_call_credentials_vtable plugin_vtable = {
-    plugin_destruct, plugin_get_request_metadata};
-
-grpc_call_credentials *grpc_metadata_credentials_create_from_plugin(
-    grpc_metadata_credentials_plugin plugin, void *reserved) {
-  grpc_plugin_credentials *c = gpr_malloc(sizeof(*c));
-  GRPC_API_TRACE("grpc_metadata_credentials_create_from_plugin(reserved=%p)", 1,
-                 (reserved));
-  GPR_ASSERT(reserved == NULL);
-  memset(c, 0, sizeof(*c));
-  c->base.type = plugin.type;
-  c->base.vtable = &plugin_vtable;
-  gpr_ref_init(&c->base.refcount, 1);
-  c->plugin = plugin;
-  return &c->base;
-}
-
-/* -- Composite channel credentials. -- */
-
-static void composite_channel_destruct(grpc_channel_credentials *creds) {
-  grpc_composite_channel_credentials *c =
-      (grpc_composite_channel_credentials *)creds;
-  grpc_channel_credentials_unref(c->inner_creds);
-  grpc_call_credentials_unref(c->call_creds);
-}
-
-static grpc_security_status composite_channel_create_security_connector(
-    grpc_channel_credentials *creds, grpc_call_credentials *call_creds,
-    const char *target, const grpc_channel_args *args,
-    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
-  grpc_composite_channel_credentials *c =
-      (grpc_composite_channel_credentials *)creds;
-  grpc_security_status status = GRPC_SECURITY_ERROR;
-
-  GPR_ASSERT(c->inner_creds != NULL && c->call_creds != NULL &&
-             c->inner_creds->vtable != NULL &&
-             c->inner_creds->vtable->create_security_connector != NULL);
-  /* If we are passed a call_creds, create a call composite to pass it
-     downstream. */
-  if (call_creds != NULL) {
-    grpc_call_credentials *composite_call_creds =
-        grpc_composite_call_credentials_create(c->call_creds, call_creds, NULL);
-    status = c->inner_creds->vtable->create_security_connector(
-        c->inner_creds, composite_call_creds, target, args, sc, new_args);
-    grpc_call_credentials_unref(composite_call_creds);
-  } else {
-    status = c->inner_creds->vtable->create_security_connector(
-        c->inner_creds, c->call_creds, target, args, sc, new_args);
-  }
-  return status;
-}
-
-static grpc_channel_credentials_vtable composite_channel_credentials_vtable = {
-    composite_channel_destruct, composite_channel_create_security_connector};
-
-grpc_channel_credentials *grpc_composite_channel_credentials_create(
-    grpc_channel_credentials *channel_creds, grpc_call_credentials *call_creds,
-    void *reserved) {
-  grpc_composite_channel_credentials *c = gpr_malloc(sizeof(*c));
-  memset(c, 0, sizeof(*c));
-  GPR_ASSERT(channel_creds != NULL && call_creds != NULL && reserved == NULL);
-  GRPC_API_TRACE(
-      "grpc_composite_channel_credentials_create(channel_creds=%p, "
-      "call_creds=%p, reserved=%p)",
-      3, (channel_creds, call_creds, reserved));
-  c->base.type = channel_creds->type;
-  c->base.vtable = &composite_channel_credentials_vtable;
-  gpr_ref_init(&c->base.refcount, 1);
-  c->inner_creds = grpc_channel_credentials_ref(channel_creds);
-  c->call_creds = grpc_call_credentials_ref(call_creds);
-  return &c->base;
-}
diff --git a/src/core/lib/security/credentials/composite/composite_credentials.c b/src/core/lib/security/credentials/composite/composite_credentials.c
new file mode 100644
index 0000000..18189a8
--- /dev/null
+++ b/src/core/lib/security/credentials/composite/composite_credentials.c
@@ -0,0 +1,262 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/security/credentials/composite/composite_credentials.h"
+
+#include <string.h>
+
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+/* -- Composite call credentials. -- */
+
+typedef struct {
+  grpc_composite_call_credentials *composite_creds;
+  size_t creds_index;
+  grpc_credentials_md_store *md_elems;
+  grpc_auth_metadata_context auth_md_context;
+  void *user_data;
+  grpc_pollset *pollset;
+  grpc_credentials_metadata_cb cb;
+} grpc_composite_call_credentials_metadata_context;
+
+static void composite_call_destruct(grpc_call_credentials *creds) {
+  grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds;
+  size_t i;
+  for (i = 0; i < c->inner.num_creds; i++) {
+    grpc_call_credentials_unref(c->inner.creds_array[i]);
+  }
+  gpr_free(c->inner.creds_array);
+}
+
+static void composite_call_md_context_destroy(
+    grpc_composite_call_credentials_metadata_context *ctx) {
+  grpc_credentials_md_store_unref(ctx->md_elems);
+  gpr_free(ctx);
+}
+
+static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data,
+                                       grpc_credentials_md *md_elems,
+                                       size_t num_md,
+                                       grpc_credentials_status status) {
+  grpc_composite_call_credentials_metadata_context *ctx =
+      (grpc_composite_call_credentials_metadata_context *)user_data;
+  if (status != GRPC_CREDENTIALS_OK) {
+    ctx->cb(exec_ctx, ctx->user_data, NULL, 0, status);
+    return;
+  }
+
+  /* Copy the metadata in the context. */
+  if (num_md > 0) {
+    size_t i;
+    for (i = 0; i < num_md; i++) {
+      grpc_credentials_md_store_add(ctx->md_elems, md_elems[i].key,
+                                    md_elems[i].value);
+    }
+  }
+
+  /* See if we need to get some more metadata. */
+  if (ctx->creds_index < ctx->composite_creds->inner.num_creds) {
+    grpc_call_credentials *inner_creds =
+        ctx->composite_creds->inner.creds_array[ctx->creds_index++];
+    grpc_call_credentials_get_request_metadata(
+        exec_ctx, inner_creds, ctx->pollset, ctx->auth_md_context,
+        composite_call_metadata_cb, ctx);
+    return;
+  }
+
+  /* We're done!. */
+  ctx->cb(exec_ctx, ctx->user_data, ctx->md_elems->entries,
+          ctx->md_elems->num_entries, GRPC_CREDENTIALS_OK);
+  composite_call_md_context_destroy(ctx);
+}
+
+static void composite_call_get_request_metadata(
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
+    grpc_pollset *pollset, grpc_auth_metadata_context auth_md_context,
+    grpc_credentials_metadata_cb cb, void *user_data) {
+  grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds;
+  grpc_composite_call_credentials_metadata_context *ctx;
+
+  ctx = gpr_malloc(sizeof(grpc_composite_call_credentials_metadata_context));
+  memset(ctx, 0, sizeof(grpc_composite_call_credentials_metadata_context));
+  ctx->auth_md_context = auth_md_context;
+  ctx->user_data = user_data;
+  ctx->cb = cb;
+  ctx->composite_creds = c;
+  ctx->pollset = pollset;
+  ctx->md_elems = grpc_credentials_md_store_create(c->inner.num_creds);
+  grpc_call_credentials_get_request_metadata(
+      exec_ctx, c->inner.creds_array[ctx->creds_index++], pollset,
+      auth_md_context, composite_call_metadata_cb, ctx);
+}
+
+static grpc_call_credentials_vtable composite_call_credentials_vtable = {
+    composite_call_destruct, composite_call_get_request_metadata};
+
+static grpc_call_credentials_array get_creds_array(
+    grpc_call_credentials **creds_addr) {
+  grpc_call_credentials_array result;
+  grpc_call_credentials *creds = *creds_addr;
+  result.creds_array = creds_addr;
+  result.num_creds = 1;
+  if (strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0) {
+    result = *grpc_composite_call_credentials_get_credentials(creds);
+  }
+  return result;
+}
+
+grpc_call_credentials *grpc_composite_call_credentials_create(
+    grpc_call_credentials *creds1, grpc_call_credentials *creds2,
+    void *reserved) {
+  size_t i;
+  size_t creds_array_byte_size;
+  grpc_call_credentials_array creds1_array;
+  grpc_call_credentials_array creds2_array;
+  grpc_composite_call_credentials *c;
+  GRPC_API_TRACE(
+      "grpc_composite_call_credentials_create(creds1=%p, creds2=%p, "
+      "reserved=%p)",
+      3, (creds1, creds2, reserved));
+  GPR_ASSERT(reserved == NULL);
+  GPR_ASSERT(creds1 != NULL);
+  GPR_ASSERT(creds2 != NULL);
+  c = gpr_malloc(sizeof(grpc_composite_call_credentials));
+  memset(c, 0, sizeof(grpc_composite_call_credentials));
+  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE;
+  c->base.vtable = &composite_call_credentials_vtable;
+  gpr_ref_init(&c->base.refcount, 1);
+  creds1_array = get_creds_array(&creds1);
+  creds2_array = get_creds_array(&creds2);
+  c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds;
+  creds_array_byte_size = c->inner.num_creds * sizeof(grpc_call_credentials *);
+  c->inner.creds_array = gpr_malloc(creds_array_byte_size);
+  memset(c->inner.creds_array, 0, creds_array_byte_size);
+  for (i = 0; i < creds1_array.num_creds; i++) {
+    grpc_call_credentials *cur_creds = creds1_array.creds_array[i];
+    c->inner.creds_array[i] = grpc_call_credentials_ref(cur_creds);
+  }
+  for (i = 0; i < creds2_array.num_creds; i++) {
+    grpc_call_credentials *cur_creds = creds2_array.creds_array[i];
+    c->inner.creds_array[i + creds1_array.num_creds] =
+        grpc_call_credentials_ref(cur_creds);
+  }
+  return &c->base;
+}
+
+const grpc_call_credentials_array *
+grpc_composite_call_credentials_get_credentials(grpc_call_credentials *creds) {
+  const grpc_composite_call_credentials *c =
+      (const grpc_composite_call_credentials *)creds;
+  GPR_ASSERT(strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0);
+  return &c->inner;
+}
+
+grpc_call_credentials *grpc_credentials_contains_type(
+    grpc_call_credentials *creds, const char *type,
+    grpc_call_credentials **composite_creds) {
+  size_t i;
+  if (strcmp(creds->type, type) == 0) {
+    if (composite_creds != NULL) *composite_creds = NULL;
+    return creds;
+  } else if (strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0) {
+    const grpc_call_credentials_array *inner_creds_array =
+        grpc_composite_call_credentials_get_credentials(creds);
+    for (i = 0; i < inner_creds_array->num_creds; i++) {
+      if (strcmp(type, inner_creds_array->creds_array[i]->type) == 0) {
+        if (composite_creds != NULL) *composite_creds = creds;
+        return inner_creds_array->creds_array[i];
+      }
+    }
+  }
+  return NULL;
+}
+
+/* -- Composite channel credentials. -- */
+
+static void composite_channel_destruct(grpc_channel_credentials *creds) {
+  grpc_composite_channel_credentials *c =
+      (grpc_composite_channel_credentials *)creds;
+  grpc_channel_credentials_unref(c->inner_creds);
+  grpc_call_credentials_unref(c->call_creds);
+}
+
+static grpc_security_status composite_channel_create_security_connector(
+    grpc_channel_credentials *creds, grpc_call_credentials *call_creds,
+    const char *target, const grpc_channel_args *args,
+    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
+  grpc_composite_channel_credentials *c =
+      (grpc_composite_channel_credentials *)creds;
+  grpc_security_status status = GRPC_SECURITY_ERROR;
+
+  GPR_ASSERT(c->inner_creds != NULL && c->call_creds != NULL &&
+             c->inner_creds->vtable != NULL &&
+             c->inner_creds->vtable->create_security_connector != NULL);
+  /* If we are passed a call_creds, create a call composite to pass it
+     downstream. */
+  if (call_creds != NULL) {
+    grpc_call_credentials *composite_call_creds =
+        grpc_composite_call_credentials_create(c->call_creds, call_creds, NULL);
+    status = c->inner_creds->vtable->create_security_connector(
+        c->inner_creds, composite_call_creds, target, args, sc, new_args);
+    grpc_call_credentials_unref(composite_call_creds);
+  } else {
+    status = c->inner_creds->vtable->create_security_connector(
+        c->inner_creds, c->call_creds, target, args, sc, new_args);
+  }
+  return status;
+}
+
+static grpc_channel_credentials_vtable composite_channel_credentials_vtable = {
+    composite_channel_destruct, composite_channel_create_security_connector};
+
+grpc_channel_credentials *grpc_composite_channel_credentials_create(
+    grpc_channel_credentials *channel_creds, grpc_call_credentials *call_creds,
+    void *reserved) {
+  grpc_composite_channel_credentials *c = gpr_malloc(sizeof(*c));
+  memset(c, 0, sizeof(*c));
+  GPR_ASSERT(channel_creds != NULL && call_creds != NULL && reserved == NULL);
+  GRPC_API_TRACE(
+      "grpc_composite_channel_credentials_create(channel_creds=%p, "
+      "call_creds=%p, reserved=%p)",
+      3, (channel_creds, call_creds, reserved));
+  c->base.type = channel_creds->type;
+  c->base.vtable = &composite_channel_credentials_vtable;
+  gpr_ref_init(&c->base.refcount, 1);
+  c->inner_creds = grpc_channel_credentials_ref(channel_creds);
+  c->call_creds = grpc_call_credentials_ref(call_creds);
+  return &c->base;
+}
diff --git a/src/core/lib/security/credentials/composite/composite_credentials.h b/src/core/lib/security/credentials/composite/composite_credentials.h
new file mode 100644
index 0000000..3e360c1
--- /dev/null
+++ b/src/core/lib/security/credentials/composite/composite_credentials.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_CREDENTIALS_H
+
+#include "src/core/lib/security/credentials/credentials.h"
+
+typedef struct {
+  grpc_call_credentials **creds_array;
+  size_t num_creds;
+} grpc_call_credentials_array;
+
+const grpc_call_credentials_array *
+grpc_composite_call_credentials_get_credentials(
+    grpc_call_credentials *composite_creds);
+
+/* Returns creds if creds is of the specified type or the inner creds of the
+   specified type (if found), if the creds is of type COMPOSITE.
+   If composite_creds is not NULL, *composite_creds will point to creds if of
+   type COMPOSITE in case of success. */
+grpc_call_credentials *grpc_credentials_contains_type(
+    grpc_call_credentials *creds, const char *type,
+    grpc_call_credentials **composite_creds);
+
+/* -- Channel composite credentials. -- */
+
+typedef struct {
+  grpc_channel_credentials base;
+  grpc_channel_credentials *inner_creds;
+  grpc_call_credentials *call_creds;
+} grpc_composite_channel_credentials;
+
+/* -- Composite credentials. -- */
+
+typedef struct {
+  grpc_call_credentials base;
+  grpc_call_credentials_array inner;
+} grpc_composite_call_credentials;
+
+#endif  // GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_CREDENTIALS_H
diff --git a/src/core/lib/security/credentials/credentials.c b/src/core/lib/security/credentials/credentials.c
new file mode 100644
index 0000000..3dde6e5
--- /dev/null
+++ b/src/core/lib/security/credentials/credentials.c
@@ -0,0 +1,231 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/security/credentials/credentials.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/http_client_filter.h"
+#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+/* -- Common. -- */
+
+grpc_credentials_metadata_request *grpc_credentials_metadata_request_create(
+    grpc_call_credentials *creds, grpc_credentials_metadata_cb cb,
+    void *user_data) {
+  grpc_credentials_metadata_request *r =
+      gpr_malloc(sizeof(grpc_credentials_metadata_request));
+  r->creds = grpc_call_credentials_ref(creds);
+  r->cb = cb;
+  r->user_data = user_data;
+  return r;
+}
+
+void grpc_credentials_metadata_request_destroy(
+    grpc_credentials_metadata_request *r) {
+  grpc_call_credentials_unref(r->creds);
+  gpr_free(r);
+}
+
+grpc_channel_credentials *grpc_channel_credentials_ref(
+    grpc_channel_credentials *creds) {
+  if (creds == NULL) return NULL;
+  gpr_ref(&creds->refcount);
+  return creds;
+}
+
+void grpc_channel_credentials_unref(grpc_channel_credentials *creds) {
+  if (creds == NULL) return;
+  if (gpr_unref(&creds->refcount)) {
+    if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds);
+    gpr_free(creds);
+  }
+}
+
+void grpc_channel_credentials_release(grpc_channel_credentials *creds) {
+  GRPC_API_TRACE("grpc_channel_credentials_release(creds=%p)", 1, (creds));
+  grpc_channel_credentials_unref(creds);
+}
+
+grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds) {
+  if (creds == NULL) return NULL;
+  gpr_ref(&creds->refcount);
+  return creds;
+}
+
+void grpc_call_credentials_unref(grpc_call_credentials *creds) {
+  if (creds == NULL) return;
+  if (gpr_unref(&creds->refcount)) {
+    if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds);
+    gpr_free(creds);
+  }
+}
+
+void grpc_call_credentials_release(grpc_call_credentials *creds) {
+  GRPC_API_TRACE("grpc_call_credentials_release(creds=%p)", 1, (creds));
+  grpc_call_credentials_unref(creds);
+}
+
+void grpc_call_credentials_get_request_metadata(
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
+    grpc_pollset *pollset, grpc_auth_metadata_context context,
+    grpc_credentials_metadata_cb cb, void *user_data) {
+  if (creds == NULL || creds->vtable->get_request_metadata == NULL) {
+    if (cb != NULL) {
+      cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK);
+    }
+    return;
+  }
+  creds->vtable->get_request_metadata(exec_ctx, creds, pollset, context, cb,
+                                      user_data);
+}
+
+grpc_security_status grpc_channel_credentials_create_security_connector(
+    grpc_channel_credentials *channel_creds, const char *target,
+    const grpc_channel_args *args, grpc_channel_security_connector **sc,
+    grpc_channel_args **new_args) {
+  *new_args = NULL;
+  if (channel_creds == NULL) {
+    return GRPC_SECURITY_ERROR;
+  }
+  GPR_ASSERT(channel_creds->vtable->create_security_connector != NULL);
+  return channel_creds->vtable->create_security_connector(
+      channel_creds, NULL, target, args, sc, new_args);
+}
+
+grpc_server_credentials *grpc_server_credentials_ref(
+    grpc_server_credentials *creds) {
+  if (creds == NULL) return NULL;
+  gpr_ref(&creds->refcount);
+  return creds;
+}
+
+void grpc_server_credentials_unref(grpc_server_credentials *creds) {
+  if (creds == NULL) return;
+  if (gpr_unref(&creds->refcount)) {
+    if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds);
+    if (creds->processor.destroy != NULL && creds->processor.state != NULL) {
+      creds->processor.destroy(creds->processor.state);
+    }
+    gpr_free(creds);
+  }
+}
+
+void grpc_server_credentials_release(grpc_server_credentials *creds) {
+  GRPC_API_TRACE("grpc_server_credentials_release(creds=%p)", 1, (creds));
+  grpc_server_credentials_unref(creds);
+}
+
+grpc_security_status grpc_server_credentials_create_security_connector(
+    grpc_server_credentials *creds, grpc_server_security_connector **sc) {
+  if (creds == NULL || creds->vtable->create_security_connector == NULL) {
+    gpr_log(GPR_ERROR, "Server credentials cannot create security context.");
+    return GRPC_SECURITY_ERROR;
+  }
+  return creds->vtable->create_security_connector(creds, sc);
+}
+
+void grpc_server_credentials_set_auth_metadata_processor(
+    grpc_server_credentials *creds, grpc_auth_metadata_processor processor) {
+  GRPC_API_TRACE(
+      "grpc_server_credentials_set_auth_metadata_processor("
+      "creds=%p, "
+      "processor=grpc_auth_metadata_processor { process: %p, state: %p })",
+      3, (creds, (void *)(intptr_t)processor.process, processor.state));
+  if (creds == NULL) return;
+  if (creds->processor.destroy != NULL && creds->processor.state != NULL) {
+    creds->processor.destroy(creds->processor.state);
+  }
+  creds->processor = processor;
+}
+
+static void server_credentials_pointer_arg_destroy(void *p) {
+  grpc_server_credentials_unref(p);
+}
+
+static void *server_credentials_pointer_arg_copy(void *p) {
+  return grpc_server_credentials_ref(p);
+}
+
+static int server_credentials_pointer_cmp(void *a, void *b) {
+  return GPR_ICMP(a, b);
+}
+
+static const grpc_arg_pointer_vtable cred_ptr_vtable = {
+    server_credentials_pointer_arg_copy, server_credentials_pointer_arg_destroy,
+    server_credentials_pointer_cmp};
+
+grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials *p) {
+  grpc_arg arg;
+  memset(&arg, 0, sizeof(grpc_arg));
+  arg.type = GRPC_ARG_POINTER;
+  arg.key = GRPC_SERVER_CREDENTIALS_ARG;
+  arg.value.pointer.p = p;
+  arg.value.pointer.vtable = &cred_ptr_vtable;
+  return arg;
+}
+
+grpc_server_credentials *grpc_server_credentials_from_arg(const grpc_arg *arg) {
+  if (strcmp(arg->key, GRPC_SERVER_CREDENTIALS_ARG) != 0) return NULL;
+  if (arg->type != GRPC_ARG_POINTER) {
+    gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type,
+            GRPC_SERVER_CREDENTIALS_ARG);
+    return NULL;
+  }
+  return arg->value.pointer.p;
+}
+
+grpc_server_credentials *grpc_find_server_credentials_in_args(
+    const grpc_channel_args *args) {
+  size_t i;
+  if (args == NULL) return NULL;
+  for (i = 0; i < args->num_args; i++) {
+    grpc_server_credentials *p =
+        grpc_server_credentials_from_arg(&args->args[i]);
+    if (p != NULL) return p;
+  }
+  return NULL;
+}
diff --git a/src/core/lib/security/credentials.h b/src/core/lib/security/credentials/credentials.h
similarity index 62%
rename from src/core/lib/security/credentials.h
rename to src/core/lib/security/credentials/credentials.h
index 0373cea..675e02b 100644
--- a/src/core/lib/security/credentials.h
+++ b/src/core/lib/security/credentials/credentials.h
@@ -31,8 +31,8 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_H
-#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_H
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H
 
 #include <grpc/grpc.h>
 #include <grpc/grpc_security.h>
@@ -41,8 +41,7 @@
 
 #include "src/core/lib/http/httpcli.h"
 #include "src/core/lib/http/parser.h"
-#include "src/core/lib/security/json_token.h"
-#include "src/core/lib/security/security_connector.h"
+#include "src/core/lib/security/transport/security_connector.h"
 
 struct grpc_http_response;
 
@@ -69,10 +68,6 @@
   "x-goog-iam-authorization-token"
 #define GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY "x-goog-iam-authority-selector"
 
-#define GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY "gcloud"
-#define GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE \
-  "application_default_credentials.json"
-
 #define GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS 60
 
 #define GRPC_COMPUTE_ENGINE_METADATA_HOST "metadata"
@@ -188,48 +183,11 @@
     grpc_pollset *pollset, grpc_auth_metadata_context context,
     grpc_credentials_metadata_cb cb, void *user_data);
 
-typedef struct {
-  grpc_call_credentials **creds_array;
-  size_t num_creds;
-} grpc_call_credentials_array;
-
-const grpc_call_credentials_array *
-grpc_composite_call_credentials_get_credentials(
-    grpc_call_credentials *composite_creds);
-
-/* Returns creds if creds is of the specified type or the inner creds of the
-   specified type (if found), if the creds is of type COMPOSITE.
-   If composite_creds is not NULL, *composite_creds will point to creds if of
-   type COMPOSITE in case of success. */
-grpc_call_credentials *grpc_credentials_contains_type(
-    grpc_call_credentials *creds, const char *type,
-    grpc_call_credentials **composite_creds);
-
-/* Exposed for testing only. */
-grpc_credentials_status
-grpc_oauth2_token_fetcher_credentials_parse_server_response(
-    const struct grpc_http_response *response,
-    grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime);
-
-void grpc_flush_cached_google_default_credentials(void);
-
 /* Metadata-only credentials with the specified key and value where
    asynchronicity can be simulated for testing. */
 grpc_call_credentials *grpc_md_only_test_credentials_create(
     const char *md_key, const char *md_value, int is_async);
 
-/* Private constructor for jwt credentials from an already parsed json key.
-   Takes ownership of the key. */
-grpc_call_credentials *
-grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
-    grpc_auth_json_key key, gpr_timespec token_lifetime);
-
-/* Private constructor for refresh token credentials from an already parsed
-   refresh token. Takes ownership of the refresh token. */
-grpc_call_credentials *
-grpc_refresh_token_credentials_create_from_auth_refresh_token(
-    grpc_auth_refresh_token token);
-
 /* --- grpc_server_credentials. --- */
 
 typedef struct {
@@ -260,118 +218,20 @@
 grpc_server_credentials *grpc_find_server_credentials_in_args(
     const grpc_channel_args *args);
 
-/* -- Fake transport security credentials. -- */
-
-/* Creates a fake transport security credentials object for testing. */
-grpc_channel_credentials *grpc_fake_transport_security_credentials_create(void);
-/* Creates a fake server transport security credentials object for testing. */
-grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
-    void);
-
-/* -- Ssl credentials. -- */
+/* -- Credentials Metadata Request. -- */
 
 typedef struct {
-  grpc_channel_credentials base;
-  grpc_ssl_config config;
-} grpc_ssl_credentials;
+  grpc_call_credentials *creds;
+  grpc_credentials_metadata_cb cb;
+  grpc_http_response response;
+  void *user_data;
+} grpc_credentials_metadata_request;
 
-typedef struct {
-  grpc_server_credentials base;
-  grpc_ssl_server_config config;
-} grpc_ssl_server_credentials;
+grpc_credentials_metadata_request *grpc_credentials_metadata_request_create(
+    grpc_call_credentials *creds, grpc_credentials_metadata_cb cb,
+    void *user_data);
 
-/* -- Channel composite credentials. -- */
+void grpc_credentials_metadata_request_destroy(
+    grpc_credentials_metadata_request *r);
 
-typedef struct {
-  grpc_channel_credentials base;
-  grpc_channel_credentials *inner_creds;
-  grpc_call_credentials *call_creds;
-} grpc_composite_channel_credentials;
-
-/* -- Jwt credentials -- */
-
-typedef struct {
-  grpc_call_credentials base;
-
-  /* Have a simple cache for now with just 1 entry. We could have a map based on
-     the service_url for a more sophisticated one. */
-  gpr_mu cache_mu;
-  struct {
-    grpc_credentials_md_store *jwt_md;
-    char *service_url;
-    gpr_timespec jwt_expiration;
-  } cached;
-
-  grpc_auth_json_key key;
-  gpr_timespec jwt_lifetime;
-} grpc_service_account_jwt_access_credentials;
-
-/* -- Oauth2TokenFetcher credentials --
-
-   This object is a base for credentials that need to acquire an oauth2 token
-   from an http service. */
-
-typedef struct grpc_credentials_metadata_request
-    grpc_credentials_metadata_request;
-
-typedef void (*grpc_fetch_oauth2_func)(grpc_exec_ctx *exec_ctx,
-                                       grpc_credentials_metadata_request *req,
-                                       grpc_httpcli_context *http_context,
-                                       grpc_pollset *pollset,
-                                       grpc_httpcli_response_cb response_cb,
-                                       gpr_timespec deadline);
-
-typedef struct {
-  grpc_call_credentials base;
-  gpr_mu mu;
-  grpc_credentials_md_store *access_token_md;
-  gpr_timespec token_expiration;
-  grpc_httpcli_context httpcli_context;
-  grpc_fetch_oauth2_func fetch_func;
-} grpc_oauth2_token_fetcher_credentials;
-
-/* -- GoogleRefreshToken credentials. -- */
-
-typedef struct {
-  grpc_oauth2_token_fetcher_credentials base;
-  grpc_auth_refresh_token refresh_token;
-} grpc_google_refresh_token_credentials;
-
-/* -- Oauth2 Access Token credentials. -- */
-
-typedef struct {
-  grpc_call_credentials base;
-  grpc_credentials_md_store *access_token_md;
-} grpc_access_token_credentials;
-
-/* --  Metadata-only Test credentials. -- */
-
-typedef struct {
-  grpc_call_credentials base;
-  grpc_credentials_md_store *md_store;
-  int is_async;
-} grpc_md_only_test_credentials;
-
-/* -- GoogleIAM credentials. -- */
-
-typedef struct {
-  grpc_call_credentials base;
-  grpc_credentials_md_store *iam_md;
-} grpc_google_iam_credentials;
-
-/* -- Composite credentials. -- */
-
-typedef struct {
-  grpc_call_credentials base;
-  grpc_call_credentials_array inner;
-} grpc_composite_call_credentials;
-
-/* -- Plugin credentials. -- */
-
-typedef struct {
-  grpc_call_credentials base;
-  grpc_metadata_credentials_plugin plugin;
-  grpc_credentials_md_store *plugin_md;
-} grpc_plugin_credentials;
-
-#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_H */
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H */
diff --git a/src/core/lib/security/credentials_metadata.c b/src/core/lib/security/credentials/credentials_metadata.c
similarity index 98%
rename from src/core/lib/security/credentials_metadata.c
rename to src/core/lib/security/credentials/credentials_metadata.c
index bd00194..6a352aa 100644
--- a/src/core/lib/security/credentials_metadata.c
+++ b/src/core/lib/security/credentials/credentials_metadata.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 
 #include <grpc/support/alloc.h>
 
diff --git a/src/core/lib/security/credentials/fake/fake_credentials.c b/src/core/lib/security/credentials/fake/fake_credentials.c
new file mode 100644
index 0000000..1ff7bd1
--- /dev/null
+++ b/src/core/lib/security/credentials/fake/fake_credentials.c
@@ -0,0 +1,139 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/security/credentials/fake/fake_credentials.h"
+
+#include <string.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/executor.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+/* -- Fake transport security credentials. -- */
+
+static grpc_security_status fake_transport_security_create_security_connector(
+    grpc_channel_credentials *c, grpc_call_credentials *call_creds,
+    const char *target, const grpc_channel_args *args,
+    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
+  *sc = grpc_fake_channel_security_connector_create(call_creds);
+  return GRPC_SECURITY_OK;
+}
+
+static grpc_security_status
+fake_transport_security_server_create_security_connector(
+    grpc_server_credentials *c, grpc_server_security_connector **sc) {
+  *sc = grpc_fake_server_security_connector_create();
+  return GRPC_SECURITY_OK;
+}
+
+static grpc_channel_credentials_vtable
+    fake_transport_security_credentials_vtable = {
+        NULL, fake_transport_security_create_security_connector};
+
+static grpc_server_credentials_vtable
+    fake_transport_security_server_credentials_vtable = {
+        NULL, fake_transport_security_server_create_security_connector};
+
+grpc_channel_credentials *grpc_fake_transport_security_credentials_create(
+    void) {
+  grpc_channel_credentials *c = gpr_malloc(sizeof(grpc_channel_credentials));
+  memset(c, 0, sizeof(grpc_channel_credentials));
+  c->type = GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
+  c->vtable = &fake_transport_security_credentials_vtable;
+  gpr_ref_init(&c->refcount, 1);
+  return c;
+}
+
+grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
+    void) {
+  grpc_server_credentials *c = gpr_malloc(sizeof(grpc_server_credentials));
+  memset(c, 0, sizeof(grpc_server_credentials));
+  c->type = GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY;
+  gpr_ref_init(&c->refcount, 1);
+  c->vtable = &fake_transport_security_server_credentials_vtable;
+  return c;
+}
+
+/* -- Metadata-only test credentials. -- */
+
+static void md_only_test_destruct(grpc_call_credentials *creds) {
+  grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds;
+  grpc_credentials_md_store_unref(c->md_store);
+}
+
+static void on_simulated_token_fetch_done(grpc_exec_ctx *exec_ctx,
+                                          void *user_data, grpc_error *error) {
+  grpc_credentials_metadata_request *r =
+      (grpc_credentials_metadata_request *)user_data;
+  grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)r->creds;
+  r->cb(exec_ctx, r->user_data, c->md_store->entries, c->md_store->num_entries,
+        GRPC_CREDENTIALS_OK);
+  grpc_credentials_metadata_request_destroy(r);
+}
+
+static void md_only_test_get_request_metadata(
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
+    grpc_pollset *pollset, grpc_auth_metadata_context context,
+    grpc_credentials_metadata_cb cb, void *user_data) {
+  grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds;
+
+  if (c->is_async) {
+    grpc_credentials_metadata_request *cb_arg =
+        grpc_credentials_metadata_request_create(creds, cb, user_data);
+    grpc_executor_push(
+        grpc_closure_create(on_simulated_token_fetch_done, cb_arg),
+        GRPC_ERROR_NONE);
+  } else {
+    cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK);
+  }
+}
+
+static grpc_call_credentials_vtable md_only_test_vtable = {
+    md_only_test_destruct, md_only_test_get_request_metadata};
+
+grpc_call_credentials *grpc_md_only_test_credentials_create(
+    const char *md_key, const char *md_value, int is_async) {
+  grpc_md_only_test_credentials *c =
+      gpr_malloc(sizeof(grpc_md_only_test_credentials));
+  memset(c, 0, sizeof(grpc_md_only_test_credentials));
+  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
+  c->base.vtable = &md_only_test_vtable;
+  gpr_ref_init(&c->base.refcount, 1);
+  c->md_store = grpc_credentials_md_store_create(1);
+  grpc_credentials_md_store_add_cstrings(c->md_store, md_key, md_value);
+  c->is_async = is_async;
+  return &c->base;
+}
diff --git a/src/core/lib/security/secure_endpoint.h b/src/core/lib/security/credentials/fake/fake_credentials.h
similarity index 64%
copy from src/core/lib/security/secure_endpoint.h
copy to src/core/lib/security/credentials/fake/fake_credentials.h
index ff1c663..e2403b5 100644
--- a/src/core/lib/security/secure_endpoint.h
+++ b/src/core/lib/security/credentials/fake/fake_credentials.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,19 +31,25 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_SECURE_ENDPOINT_H
-#define GRPC_CORE_LIB_SECURITY_SECURE_ENDPOINT_H
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_CREDENTIALS_H
 
-#include <grpc/support/slice.h>
-#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/security/credentials/credentials.h"
 
-struct tsi_frame_protector;
+/* -- Fake transport security credentials. -- */
 
-extern int grpc_trace_secure_endpoint;
+/* Creates a fake transport security credentials object for testing. */
+grpc_channel_credentials *grpc_fake_transport_security_credentials_create(void);
+/* Creates a fake server transport security credentials object for testing. */
+grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
+    void);
 
-/* Takes ownership of protector and to_wrap, and refs leftover_slices. */
-grpc_endpoint *grpc_secure_endpoint_create(
-    struct tsi_frame_protector *protector, grpc_endpoint *to_wrap,
-    gpr_slice *leftover_slices, size_t leftover_nslices);
+/* --  Metadata-only Test credentials. -- */
 
-#endif /* GRPC_CORE_LIB_SECURITY_SECURE_ENDPOINT_H */
+typedef struct {
+  grpc_call_credentials base;
+  grpc_credentials_md_store *md_store;
+  int is_async;
+} grpc_md_only_test_credentials;
+
+#endif  // GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_CREDENTIALS_H
diff --git a/src/core/lib/security/credentials_posix.c b/src/core/lib/security/credentials/google_default/credentials_posix.c
similarity index 95%
rename from src/core/lib/security/credentials_posix.c
rename to src/core/lib/security/credentials/google_default/credentials_posix.c
index a07de18..42c9d7f 100644
--- a/src/core/lib/security/credentials_posix.c
+++ b/src/core/lib/security/credentials/google_default/credentials_posix.c
@@ -35,7 +35,7 @@
 
 #ifdef GPR_POSIX_FILE
 
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/google_default/google_default_credentials.h"
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
diff --git a/src/core/lib/security/credentials_win32.c b/src/core/lib/security/credentials/google_default/credentials_win32.c
similarity index 95%
rename from src/core/lib/security/credentials_win32.c
rename to src/core/lib/security/credentials/google_default/credentials_win32.c
index d29847a..cd8b480 100644
--- a/src/core/lib/security/credentials_win32.c
+++ b/src/core/lib/security/credentials/google_default/credentials_win32.c
@@ -35,7 +35,7 @@
 
 #ifdef GPR_WIN32
 
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/google_default/google_default_credentials.h"
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
diff --git a/src/core/lib/security/google_default_credentials.c b/src/core/lib/security/credentials/google_default/google_default_credentials.c
similarity index 71%
rename from src/core/lib/security/google_default_credentials.c
rename to src/core/lib/security/credentials/google_default/google_default_credentials.c
index 236f1d7..4656684 100644
--- a/src/core/lib/security/google_default_credentials.c
+++ b/src/core/lib/security/credentials/google_default/google_default_credentials.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 
 #include <string.h>
 
@@ -41,8 +41,11 @@
 
 #include "src/core/lib/http/httpcli.h"
 #include "src/core/lib/http/parser.h"
+#include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
+#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/load_file.h"
+#include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/api_trace.h"
 
 /* -- Constants. -- */
@@ -63,18 +66,20 @@
   grpc_pollset *pollset;
   int is_done;
   int success;
+  grpc_http_response response;
 } compute_engine_detector;
 
-static void on_compute_engine_detection_http_response(
-    grpc_exec_ctx *exec_ctx, void *user_data,
-    const grpc_http_response *response) {
+static void on_compute_engine_detection_http_response(grpc_exec_ctx *exec_ctx,
+                                                      void *user_data,
+                                                      grpc_error *error) {
   compute_engine_detector *detector = (compute_engine_detector *)user_data;
-  if (response != NULL && response->status == 200 && response->hdr_count > 0) {
+  if (error == GRPC_ERROR_NONE && detector->response.status == 200 &&
+      detector->response.hdr_count > 0) {
     /* Internet providers can return a generic response to all requests, so
        it is necessary to check that metadata header is present also. */
     size_t i;
-    for (i = 0; i < response->hdr_count; i++) {
-      grpc_http_header *header = &response->hdrs[i];
+    for (i = 0; i < detector->response.hdr_count; i++) {
+      grpc_http_header *header = &detector->response.hdrs[i];
       if (strcmp(header->key, "Metadata-Flavor") == 0 &&
           strcmp(header->value, "Google") == 0) {
         detector->success = 1;
@@ -84,11 +89,11 @@
   }
   gpr_mu_lock(g_polling_mu);
   detector->is_done = 1;
-  grpc_pollset_kick(detector->pollset, NULL);
+  GRPC_LOG_IF_ERROR("Pollset kick", grpc_pollset_kick(detector->pollset, NULL));
   gpr_mu_unlock(g_polling_mu);
 }
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool s) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, grpc_error *e) {
   grpc_pollset_destroy(p);
 }
 
@@ -117,18 +122,24 @@
   grpc_httpcli_get(
       &exec_ctx, &context, detector.pollset, &request,
       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay),
-      on_compute_engine_detection_http_response, &detector);
+      grpc_closure_create(on_compute_engine_detection_http_response, &detector),
+      &detector.response);
 
-  grpc_exec_ctx_finish(&exec_ctx);
+  grpc_exec_ctx_flush(&exec_ctx);
 
   /* Block until we get the response. This is not ideal but this should only be
      called once for the lifetime of the process by the default credentials. */
   gpr_mu_lock(g_polling_mu);
   while (!detector.is_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, detector.pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    if (!GRPC_LOG_IF_ERROR(
+            "pollset_work",
+            grpc_pollset_work(&exec_ctx, detector.pollset, &worker,
+                              gpr_now(GPR_CLOCK_MONOTONIC),
+                              gpr_inf_future(GPR_CLOCK_MONOTONIC)))) {
+      detector.is_done = 1;
+      detector.success = 0;
+    }
   }
   gpr_mu_unlock(g_polling_mu);
 
@@ -144,19 +155,31 @@
 }
 
 /* Takes ownership of creds_path if not NULL. */
-static grpc_call_credentials *create_default_creds_from_path(char *creds_path) {
+static grpc_error *create_default_creds_from_path(
+    char *creds_path, grpc_call_credentials **creds) {
   grpc_json *json = NULL;
   grpc_auth_json_key key;
   grpc_auth_refresh_token token;
   grpc_call_credentials *result = NULL;
   gpr_slice creds_data = gpr_empty_slice();
-  int file_ok = 0;
-  if (creds_path == NULL) goto end;
-  creds_data = gpr_load_file(creds_path, 0, &file_ok);
-  if (!file_ok) goto end;
+  grpc_error *error = GRPC_ERROR_NONE;
+  if (creds_path == NULL) {
+    error = GRPC_ERROR_CREATE("creds_path unset");
+    goto end;
+  }
+  error = gpr_load_file(creds_path, 0, &creds_data);
+  if (error != GRPC_ERROR_NONE) {
+    goto end;
+  }
   json = grpc_json_parse_string_with_len(
       (char *)GPR_SLICE_START_PTR(creds_data), GPR_SLICE_LENGTH(creds_data));
-  if (json == NULL) goto end;
+  if (json == NULL) {
+    char *dump = gpr_dump_slice(creds_data, GPR_DUMP_HEX | GPR_DUMP_ASCII);
+    error = grpc_error_set_str(GRPC_ERROR_CREATE("Failed to parse JSON"),
+                               GRPC_ERROR_STR_RAW_BYTES, dump);
+    gpr_free(dump);
+    goto end;
+  }
 
   /* First, try an auth json key. */
   key = grpc_auth_json_key_create_from_json(json);
@@ -164,6 +187,11 @@
     result =
         grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
             key, grpc_max_auth_token_lifetime());
+    if (result == NULL) {
+      error = GRPC_ERROR_CREATE(
+          "grpc_service_account_jwt_access_credentials_create_from_auth_json_"
+          "key failed");
+    }
     goto end;
   }
 
@@ -172,19 +200,28 @@
   if (grpc_auth_refresh_token_is_valid(&token)) {
     result =
         grpc_refresh_token_credentials_create_from_auth_refresh_token(token);
+    if (result == NULL) {
+      error = GRPC_ERROR_CREATE(
+          "grpc_refresh_token_credentials_create_from_auth_refresh_token "
+          "failed");
+    }
     goto end;
   }
 
 end:
+  GPR_ASSERT((result == NULL) + (error == GRPC_ERROR_NONE) == 1);
   if (creds_path != NULL) gpr_free(creds_path);
   gpr_slice_unref(creds_data);
   if (json != NULL) grpc_json_destroy(json);
-  return result;
+  *creds = result;
+  return error;
 }
 
 grpc_channel_credentials *grpc_google_default_credentials_create(void) {
   grpc_channel_credentials *result = NULL;
   grpc_call_credentials *call_creds = NULL;
+  grpc_error *error = GRPC_ERROR_CREATE("Failed to create Google credentials");
+  grpc_error *err;
 
   GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ());
 
@@ -198,14 +235,16 @@
   }
 
   /* First, try the environment variable. */
-  call_creds = create_default_creds_from_path(
-      gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR));
-  if (call_creds != NULL) goto end;
+  err = create_default_creds_from_path(
+      gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR), &call_creds);
+  if (err == GRPC_ERROR_NONE) goto end;
+  error = grpc_error_add_child(error, err);
 
   /* Then the well-known file. */
-  call_creds = create_default_creds_from_path(
-      grpc_get_well_known_google_credentials_file_path());
-  if (call_creds != NULL) goto end;
+  err = create_default_creds_from_path(
+      grpc_get_well_known_google_credentials_file_path(), &call_creds);
+  if (err == GRPC_ERROR_NONE) goto end;
+  error = grpc_error_add_child(error, err);
 
   /* At last try to see if we're on compute engine (do the detection only once
      since it requires a network test). */
@@ -214,6 +253,10 @@
     compute_engine_detection_done = 1;
     if (need_compute_engine_creds) {
       call_creds = grpc_google_compute_engine_credentials_create(NULL);
+      if (call_creds == NULL) {
+        error = grpc_error_add_child(
+            error, GRPC_ERROR_CREATE("Failed to get credentials from network"));
+      }
     }
   }
 
@@ -237,6 +280,11 @@
     }
   }
   gpr_mu_unlock(&g_state_mu);
+  if (result == NULL) {
+    GRPC_LOG_IF_ERROR("grpc_google_default_credentials_create", error);
+  } else {
+    GRPC_ERROR_UNREF(error);
+  }
   return result;
 }
 
diff --git a/src/core/lib/security/auth_filters.h b/src/core/lib/security/credentials/google_default/google_default_credentials.h
similarity index 74%
copy from src/core/lib/security/auth_filters.h
copy to src/core/lib/security/credentials/google_default/google_default_credentials.h
index 7fb56c3..838989f 100644
--- a/src/core/lib/security/auth_filters.h
+++ b/src/core/lib/security/credentials/google_default/google_default_credentials.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,12 +31,15 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
-#define GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_CREDENTIALS_H
 
-#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/security/credentials/credentials.h"
 
-extern const grpc_channel_filter grpc_client_auth_filter;
-extern const grpc_channel_filter grpc_server_auth_filter;
+#define GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY "gcloud"
+#define GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE \
+  "application_default_credentials.json"
 
-#endif /* GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H */
+void grpc_flush_cached_google_default_credentials(void);
+
+#endif  // GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_CREDENTIALS_H
diff --git a/src/core/lib/security/credentials/iam/iam_credentials.c b/src/core/lib/security/credentials/iam/iam_credentials.c
new file mode 100644
index 0000000..89defa7
--- /dev/null
+++ b/src/core/lib/security/credentials/iam/iam_credentials.c
@@ -0,0 +1,85 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/security/credentials/iam/iam_credentials.h"
+
+#include <string.h>
+
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+static void iam_destruct(grpc_call_credentials *creds) {
+  grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds;
+  grpc_credentials_md_store_unref(c->iam_md);
+}
+
+static void iam_get_request_metadata(grpc_exec_ctx *exec_ctx,
+                                     grpc_call_credentials *creds,
+                                     grpc_pollset *pollset,
+                                     grpc_auth_metadata_context context,
+                                     grpc_credentials_metadata_cb cb,
+                                     void *user_data) {
+  grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds;
+  cb(exec_ctx, user_data, c->iam_md->entries, c->iam_md->num_entries,
+     GRPC_CREDENTIALS_OK);
+}
+
+static grpc_call_credentials_vtable iam_vtable = {iam_destruct,
+                                                  iam_get_request_metadata};
+
+grpc_call_credentials *grpc_google_iam_credentials_create(
+    const char *token, const char *authority_selector, void *reserved) {
+  grpc_google_iam_credentials *c;
+  GRPC_API_TRACE(
+      "grpc_iam_credentials_create(token=%s, authority_selector=%s, "
+      "reserved=%p)",
+      3, (token, authority_selector, reserved));
+  GPR_ASSERT(reserved == NULL);
+  GPR_ASSERT(token != NULL);
+  GPR_ASSERT(authority_selector != NULL);
+  c = gpr_malloc(sizeof(grpc_google_iam_credentials));
+  memset(c, 0, sizeof(grpc_google_iam_credentials));
+  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_IAM;
+  c->base.vtable = &iam_vtable;
+  gpr_ref_init(&c->base.refcount, 1);
+  c->iam_md = grpc_credentials_md_store_create(2);
+  grpc_credentials_md_store_add_cstrings(
+      c->iam_md, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, token);
+  grpc_credentials_md_store_add_cstrings(
+      c->iam_md, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector);
+  return &c->base;
+}
diff --git a/src/core/lib/security/auth_filters.h b/src/core/lib/security/credentials/iam/iam_credentials.h
similarity index 79%
copy from src/core/lib/security/auth_filters.h
copy to src/core/lib/security/credentials/iam/iam_credentials.h
index 7fb56c3..06b4db8 100644
--- a/src/core/lib/security/auth_filters.h
+++ b/src/core/lib/security/credentials/iam/iam_credentials.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,12 +31,14 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
-#define GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_CREDENTIALS_H
 
-#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/security/credentials/credentials.h"
 
-extern const grpc_channel_filter grpc_client_auth_filter;
-extern const grpc_channel_filter grpc_server_auth_filter;
+typedef struct {
+  grpc_call_credentials base;
+  grpc_credentials_md_store *iam_md;
+} grpc_google_iam_credentials;
 
-#endif /* GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H */
+#endif  // GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_CREDENTIALS_H
diff --git a/src/core/lib/security/json_token.c b/src/core/lib/security/credentials/jwt/json_token.c
similarity index 74%
rename from src/core/lib/security/json_token.c
rename to src/core/lib/security/credentials/jwt/json_token.c
index d5bc2c8..354c131 100644
--- a/src/core/lib/security/json_token.c
+++ b/src/core/lib/security/credentials/jwt/json_token.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/lib/security/json_token.h"
+#include "src/core/lib/security/credentials/jwt/json_token.h"
 
 #include <string.h>
 
@@ -39,7 +39,8 @@
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/lib/security/b64.h"
+#include "src/core/lib/security/util/b64.h"
+#include "src/core/lib/security/util/json_util.h"
 #include "src/core/lib/support/string.h"
 
 #include <openssl/bio.h>
@@ -66,28 +67,6 @@
 
 /* --- grpc_auth_json_key. --- */
 
-static const char *json_get_string_property(const grpc_json *json,
-                                            const char *prop_name) {
-  grpc_json *child;
-  for (child = json->child; child != NULL; child = child->next) {
-    if (strcmp(child->key, prop_name) == 0) break;
-  }
-  if (child == NULL || child->type != GRPC_JSON_STRING) {
-    gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name);
-    return NULL;
-  }
-  return child->value;
-}
-
-static int set_json_key_string_property(const grpc_json *json,
-                                        const char *prop_name,
-                                        char **json_key_field) {
-  const char *prop_value = json_get_string_property(json, prop_name);
-  if (prop_value == NULL) return 0;
-  *json_key_field = gpr_strdup(prop_value);
-  return 1;
-}
-
 int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key) {
   return (json_key != NULL) &&
          strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID);
@@ -106,22 +85,22 @@
     goto end;
   }
 
-  prop_value = json_get_string_property(json, "type");
+  prop_value = grpc_json_get_string_property(json, "type");
   if (prop_value == NULL ||
       strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT)) {
     goto end;
   }
   result.type = GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT;
 
-  if (!set_json_key_string_property(json, "private_key_id",
-                                    &result.private_key_id) ||
-      !set_json_key_string_property(json, "client_id", &result.client_id) ||
-      !set_json_key_string_property(json, "client_email",
-                                    &result.client_email)) {
+  if (!grpc_copy_json_string_property(json, "private_key_id",
+                                      &result.private_key_id) ||
+      !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
+      !grpc_copy_json_string_property(json, "client_email",
+                                      &result.client_email)) {
     goto end;
   }
 
-  prop_value = json_get_string_property(json, "private_key");
+  prop_value = grpc_json_get_string_property(json, "private_key");
   if (prop_value == NULL) {
     goto end;
   }
@@ -339,73 +318,3 @@
     grpc_jwt_encode_and_sign_override func) {
   g_jwt_encode_and_sign_override = func;
 }
-
-/* --- grpc_auth_refresh_token --- */
-
-int grpc_auth_refresh_token_is_valid(
-    const grpc_auth_refresh_token *refresh_token) {
-  return (refresh_token != NULL) &&
-         strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID);
-}
-
-grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
-    const grpc_json *json) {
-  grpc_auth_refresh_token result;
-  const char *prop_value;
-  int success = 0;
-
-  memset(&result, 0, sizeof(grpc_auth_refresh_token));
-  result.type = GRPC_AUTH_JSON_TYPE_INVALID;
-  if (json == NULL) {
-    gpr_log(GPR_ERROR, "Invalid json.");
-    goto end;
-  }
-
-  prop_value = json_get_string_property(json, "type");
-  if (prop_value == NULL ||
-      strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) {
-    goto end;
-  }
-  result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;
-
-  if (!set_json_key_string_property(json, "client_secret",
-                                    &result.client_secret) ||
-      !set_json_key_string_property(json, "client_id", &result.client_id) ||
-      !set_json_key_string_property(json, "refresh_token",
-                                    &result.refresh_token)) {
-    goto end;
-  }
-  success = 1;
-
-end:
-  if (!success) grpc_auth_refresh_token_destruct(&result);
-  return result;
-}
-
-grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
-    const char *json_string) {
-  char *scratchpad = gpr_strdup(json_string);
-  grpc_json *json = grpc_json_parse_string(scratchpad);
-  grpc_auth_refresh_token result =
-      grpc_auth_refresh_token_create_from_json(json);
-  if (json != NULL) grpc_json_destroy(json);
-  gpr_free(scratchpad);
-  return result;
-}
-
-void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token) {
-  if (refresh_token == NULL) return;
-  refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID;
-  if (refresh_token->client_id != NULL) {
-    gpr_free(refresh_token->client_id);
-    refresh_token->client_id = NULL;
-  }
-  if (refresh_token->client_secret != NULL) {
-    gpr_free(refresh_token->client_secret);
-    refresh_token->client_secret = NULL;
-  }
-  if (refresh_token->refresh_token != NULL) {
-    gpr_free(refresh_token->refresh_token);
-    refresh_token->refresh_token = NULL;
-  }
-}
diff --git a/src/core/lib/security/json_token.h b/src/core/lib/security/credentials/jwt/json_token.h
similarity index 73%
rename from src/core/lib/security/json_token.h
rename to src/core/lib/security/credentials/jwt/json_token.h
index 123fa65..07fc5bf 100644
--- a/src/core/lib/security/json_token.h
+++ b/src/core/lib/security/credentials/jwt/json_token.h
@@ -31,8 +31,8 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_JSON_TOKEN_H
-#define GRPC_CORE_LIB_SECURITY_JSON_TOKEN_H
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H
 
 #include <grpc/support/slice.h>
 #include <openssl/rsa.h>
@@ -43,10 +43,6 @@
 
 #define GRPC_JWT_OAUTH2_AUDIENCE "https://www.googleapis.com/oauth2/v3/token"
 
-#define GRPC_AUTH_JSON_TYPE_INVALID "invalid"
-#define GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT "service_account"
-#define GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER "authorized_user"
-
 /* --- auth_json_key parsing. --- */
 
 typedef struct {
@@ -89,30 +85,4 @@
 void grpc_jwt_encode_and_sign_set_override(
     grpc_jwt_encode_and_sign_override func);
 
-/* --- auth_refresh_token parsing. --- */
-
-typedef struct {
-  const char *type;
-  char *client_id;
-  char *client_secret;
-  char *refresh_token;
-} grpc_auth_refresh_token;
-
-/* Returns 1 if the object is valid, 0 otherwise. */
-int grpc_auth_refresh_token_is_valid(
-    const grpc_auth_refresh_token *refresh_token);
-
-/* Creates a refresh token object from string. Returns an invalid object if a
-   parsing error has been encountered. */
-grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
-    const char *json_string);
-
-/* Creates a refresh token object from parsed json. Returns an invalid object if
-   a parsing error has been encountered. */
-grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
-    const grpc_json *json);
-
-/* Destructs the object. */
-void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token);
-
-#endif /* GRPC_CORE_LIB_SECURITY_JSON_TOKEN_H */
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H */
diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.c b/src/core/lib/security/credentials/jwt/jwt_credentials.c
new file mode 100644
index 0000000..8755a96
--- /dev/null
+++ b/src/core/lib/security/credentials/jwt/jwt_credentials.c
@@ -0,0 +1,160 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
+
+#include <string.h>
+
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+static void jwt_reset_cache(grpc_service_account_jwt_access_credentials *c) {
+  if (c->cached.jwt_md != NULL) {
+    grpc_credentials_md_store_unref(c->cached.jwt_md);
+    c->cached.jwt_md = NULL;
+  }
+  if (c->cached.service_url != NULL) {
+    gpr_free(c->cached.service_url);
+    c->cached.service_url = NULL;
+  }
+  c->cached.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
+}
+
+static void jwt_destruct(grpc_call_credentials *creds) {
+  grpc_service_account_jwt_access_credentials *c =
+      (grpc_service_account_jwt_access_credentials *)creds;
+  grpc_auth_json_key_destruct(&c->key);
+  jwt_reset_cache(c);
+  gpr_mu_destroy(&c->cache_mu);
+}
+
+static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx,
+                                     grpc_call_credentials *creds,
+                                     grpc_pollset *pollset,
+                                     grpc_auth_metadata_context context,
+                                     grpc_credentials_metadata_cb cb,
+                                     void *user_data) {
+  grpc_service_account_jwt_access_credentials *c =
+      (grpc_service_account_jwt_access_credentials *)creds;
+  gpr_timespec refresh_threshold = gpr_time_from_seconds(
+      GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
+
+  /* See if we can return a cached jwt. */
+  grpc_credentials_md_store *jwt_md = NULL;
+  {
+    gpr_mu_lock(&c->cache_mu);
+    if (c->cached.service_url != NULL &&
+        strcmp(c->cached.service_url, context.service_url) == 0 &&
+        c->cached.jwt_md != NULL &&
+        (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration,
+                                   gpr_now(GPR_CLOCK_REALTIME)),
+                      refresh_threshold) > 0)) {
+      jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md);
+    }
+    gpr_mu_unlock(&c->cache_mu);
+  }
+
+  if (jwt_md == NULL) {
+    char *jwt = NULL;
+    /* Generate a new jwt. */
+    gpr_mu_lock(&c->cache_mu);
+    jwt_reset_cache(c);
+    jwt = grpc_jwt_encode_and_sign(&c->key, context.service_url,
+                                   c->jwt_lifetime, NULL);
+    if (jwt != NULL) {
+      char *md_value;
+      gpr_asprintf(&md_value, "Bearer %s", jwt);
+      gpr_free(jwt);
+      c->cached.jwt_expiration =
+          gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c->jwt_lifetime);
+      c->cached.service_url = gpr_strdup(context.service_url);
+      c->cached.jwt_md = grpc_credentials_md_store_create(1);
+      grpc_credentials_md_store_add_cstrings(
+          c->cached.jwt_md, GRPC_AUTHORIZATION_METADATA_KEY, md_value);
+      gpr_free(md_value);
+      jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md);
+    }
+    gpr_mu_unlock(&c->cache_mu);
+  }
+
+  if (jwt_md != NULL) {
+    cb(exec_ctx, user_data, jwt_md->entries, jwt_md->num_entries,
+       GRPC_CREDENTIALS_OK);
+    grpc_credentials_md_store_unref(jwt_md);
+  } else {
+    cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR);
+  }
+}
+
+static grpc_call_credentials_vtable jwt_vtable = {jwt_destruct,
+                                                  jwt_get_request_metadata};
+
+grpc_call_credentials *
+grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
+    grpc_auth_json_key key, gpr_timespec token_lifetime) {
+  grpc_service_account_jwt_access_credentials *c;
+  if (!grpc_auth_json_key_is_valid(&key)) {
+    gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
+    return NULL;
+  }
+  c = gpr_malloc(sizeof(grpc_service_account_jwt_access_credentials));
+  memset(c, 0, sizeof(grpc_service_account_jwt_access_credentials));
+  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_JWT;
+  gpr_ref_init(&c->base.refcount, 1);
+  c->base.vtable = &jwt_vtable;
+  c->key = key;
+  c->jwt_lifetime = token_lifetime;
+  gpr_mu_init(&c->cache_mu);
+  jwt_reset_cache(c);
+  return &c->base;
+}
+
+grpc_call_credentials *grpc_service_account_jwt_access_credentials_create(
+    const char *json_key, gpr_timespec token_lifetime, void *reserved) {
+  GRPC_API_TRACE(
+      "grpc_service_account_jwt_access_credentials_create("
+      "json_key=%s, "
+      "token_lifetime="
+      "gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, "
+      "reserved=%p)",
+      5,
+      (json_key, (long long)token_lifetime.tv_sec, (int)token_lifetime.tv_nsec,
+       (int)token_lifetime.clock_type, reserved));
+  GPR_ASSERT(reserved == NULL);
+  return grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
+      grpc_auth_json_key_create_from_string(json_key), token_lifetime);
+}
diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.h b/src/core/lib/security/credentials/jwt/jwt_credentials.h
new file mode 100644
index 0000000..6fba3df
--- /dev/null
+++ b/src/core/lib/security/credentials/jwt/jwt_credentials.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_CREDENTIALS_H
+
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/credentials/jwt/json_token.h"
+
+typedef struct {
+  grpc_call_credentials base;
+
+  // Have a simple cache for now with just 1 entry. We could have a map based on
+  // the service_url for a more sophisticated one.
+  gpr_mu cache_mu;
+  struct {
+    grpc_credentials_md_store *jwt_md;
+    char *service_url;
+    gpr_timespec jwt_expiration;
+  } cached;
+
+  grpc_auth_json_key key;
+  gpr_timespec jwt_lifetime;
+} grpc_service_account_jwt_access_credentials;
+
+// Private constructor for jwt credentials from an already parsed json key.
+// Takes ownership of the key.
+grpc_call_credentials *
+grpc_service_account_jwt_access_credentials_create_from_auth_json_key(
+    grpc_auth_json_key key, gpr_timespec token_lifetime);
+
+#endif  // GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_CREDENTIALS_H
diff --git a/src/core/lib/security/jwt_verifier.c b/src/core/lib/security/credentials/jwt/jwt_verifier.c
similarity index 96%
rename from src/core/lib/security/jwt_verifier.c
rename to src/core/lib/security/credentials/jwt/jwt_verifier.c
index 0e01229..bdee6c8 100644
--- a/src/core/lib/security/jwt_verifier.c
+++ b/src/core/lib/security/credentials/jwt/jwt_verifier.c
@@ -31,19 +31,20 @@
  *
  */
 
-#include "src/core/lib/security/jwt_verifier.h"
+#include "src/core/lib/security/credentials/jwt/jwt_verifier.h"
 
 #include <limits.h>
 #include <string.h>
 
 #include "src/core/lib/http/httpcli.h"
-#include "src/core/lib/security/b64.h"
+#include "src/core/lib/security/util/b64.h"
 #include "src/core/lib/tsi/ssl_types.h"
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 #include <grpc/support/sync.h>
+#include <grpc/support/useful.h>
 #include <openssl/pem.h>
 
 /* --- Utils. --- */
@@ -329,6 +330,7 @@
   gpr_slice signed_data;
   void *user_data;
   grpc_jwt_verification_done_cb user_cb;
+  grpc_http_response responses[2];
 } verifier_cb_ctx;
 
 /* Takes ownership of the header, claims and signature. */
@@ -357,6 +359,9 @@
   gpr_slice_unref(ctx->signature);
   gpr_slice_unref(ctx->signed_data);
   jose_header_destroy(ctx->header);
+  for (size_t i = 0; i < GPR_ARRAY_SIZE(ctx->responses); i++) {
+    grpc_http_response_destroy(&ctx->responses[i]);
+  }
   /* TODO: see what to do with claims... */
   gpr_free(ctx);
 }
@@ -571,9 +576,9 @@
 }
 
 static void on_keys_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
-                              const grpc_httpcli_response *response) {
-  grpc_json *json = json_from_http(response);
+                              grpc_error *error) {
   verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data;
+  grpc_json *json = json_from_http(&ctx->responses[1]);
   EVP_PKEY *verification_key = NULL;
   grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR;
   grpc_jwt_claims *claims = NULL;
@@ -612,10 +617,11 @@
 }
 
 static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data,
-                                       const grpc_httpcli_response *response) {
+                                       grpc_error *error) {
   const grpc_json *cur;
-  grpc_json *json = json_from_http(response);
   verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data;
+  const grpc_http_response *response = &ctx->responses[0];
+  grpc_json *json = json_from_http(response);
   grpc_httpcli_request req;
   const char *jwks_uri;
 
@@ -641,10 +647,11 @@
   } else {
     *(req.host + (req.http.path - jwks_uri)) = '\0';
   }
+
   grpc_httpcli_get(
       exec_ctx, &ctx->verifier->http_ctx, ctx->pollset, &req,
       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
-      on_keys_retrieved, ctx);
+      grpc_closure_create(on_keys_retrieved, ctx), &ctx->responses[1]);
   grpc_json_destroy(json);
   gpr_free(req.host);
   return;
@@ -686,12 +693,13 @@
 static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx,
                                     verifier_cb_ctx *ctx) {
   const char *at_sign;
-  grpc_httpcli_response_cb http_cb;
+  grpc_closure *http_cb;
   char *path_prefix = NULL;
   const char *iss;
   grpc_httpcli_request req;
   memset(&req, 0, sizeof(grpc_httpcli_request));
   req.handshaker = &grpc_httpcli_ssl;
+  int rsp_idx;
 
   GPR_ASSERT(ctx != NULL && ctx->header != NULL && ctx->claims != NULL);
   iss = ctx->claims->iss;
@@ -730,7 +738,8 @@
       *(path_prefix++) = '\0';
       gpr_asprintf(&req.http.path, "/%s/%s", path_prefix, iss);
     }
-    http_cb = on_keys_retrieved;
+    http_cb = grpc_closure_create(on_keys_retrieved, ctx);
+    rsp_idx = 1;
   } else {
     req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss);
     path_prefix = strchr(req.host, '/');
@@ -741,13 +750,14 @@
       gpr_asprintf(&req.http.path, "/%s%s", path_prefix,
                    GRPC_OPENID_CONFIG_URL_SUFFIX);
     }
-    http_cb = on_openid_config_retrieved;
+    http_cb = grpc_closure_create(on_openid_config_retrieved, ctx);
+    rsp_idx = 0;
   }
 
   grpc_httpcli_get(
       exec_ctx, &ctx->verifier->http_ctx, ctx->pollset, &req,
       gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
-      http_cb, ctx);
+      http_cb, &ctx->responses[rsp_idx]);
   gpr_free(req.host);
   gpr_free(req.http.path);
   return;
diff --git a/src/core/lib/security/jwt_verifier.h b/src/core/lib/security/credentials/jwt/jwt_verifier.h
similarity index 96%
rename from src/core/lib/security/jwt_verifier.h
rename to src/core/lib/security/credentials/jwt/jwt_verifier.h
index 98a4f6b..b0f6d1c 100644
--- a/src/core/lib/security/jwt_verifier.h
+++ b/src/core/lib/security/credentials/jwt/jwt_verifier.h
@@ -31,8 +31,8 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_JWT_VERIFIER_H
-#define GRPC_CORE_LIB_SECURITY_JWT_VERIFIER_H
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H
 
 #include "src/core/lib/iomgr/pollset.h"
 #include "src/core/lib/json/json.h"
@@ -133,4 +133,4 @@
 grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims,
                                                const char *audience);
 
-#endif /* GRPC_CORE_LIB_SECURITY_JWT_VERIFIER_H */
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H */
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
new file mode 100644
index 0000000..671dea3
--- /dev/null
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c
@@ -0,0 +1,431 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
+
+#include <string.h>
+
+#include "src/core/lib/security/util/json_util.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+//
+// Auth Refresh Token.
+//
+
+int grpc_auth_refresh_token_is_valid(
+    const grpc_auth_refresh_token *refresh_token) {
+  return (refresh_token != NULL) &&
+         strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID);
+}
+
+grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
+    const grpc_json *json) {
+  grpc_auth_refresh_token result;
+  const char *prop_value;
+  int success = 0;
+
+  memset(&result, 0, sizeof(grpc_auth_refresh_token));
+  result.type = GRPC_AUTH_JSON_TYPE_INVALID;
+  if (json == NULL) {
+    gpr_log(GPR_ERROR, "Invalid json.");
+    goto end;
+  }
+
+  prop_value = grpc_json_get_string_property(json, "type");
+  if (prop_value == NULL ||
+      strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) {
+    goto end;
+  }
+  result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER;
+
+  if (!grpc_copy_json_string_property(json, "client_secret",
+                                      &result.client_secret) ||
+      !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
+      !grpc_copy_json_string_property(json, "refresh_token",
+                                      &result.refresh_token)) {
+    goto end;
+  }
+  success = 1;
+
+end:
+  if (!success) grpc_auth_refresh_token_destruct(&result);
+  return result;
+}
+
+grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
+    const char *json_string) {
+  char *scratchpad = gpr_strdup(json_string);
+  grpc_json *json = grpc_json_parse_string(scratchpad);
+  grpc_auth_refresh_token result =
+      grpc_auth_refresh_token_create_from_json(json);
+  if (json != NULL) grpc_json_destroy(json);
+  gpr_free(scratchpad);
+  return result;
+}
+
+void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token) {
+  if (refresh_token == NULL) return;
+  refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID;
+  if (refresh_token->client_id != NULL) {
+    gpr_free(refresh_token->client_id);
+    refresh_token->client_id = NULL;
+  }
+  if (refresh_token->client_secret != NULL) {
+    gpr_free(refresh_token->client_secret);
+    refresh_token->client_secret = NULL;
+  }
+  if (refresh_token->refresh_token != NULL) {
+    gpr_free(refresh_token->refresh_token);
+    refresh_token->refresh_token = NULL;
+  }
+}
+
+//
+// Oauth2 Token Fetcher credentials.
+//
+
+static void oauth2_token_fetcher_destruct(grpc_call_credentials *creds) {
+  grpc_oauth2_token_fetcher_credentials *c =
+      (grpc_oauth2_token_fetcher_credentials *)creds;
+  grpc_credentials_md_store_unref(c->access_token_md);
+  gpr_mu_destroy(&c->mu);
+  grpc_httpcli_context_destroy(&c->httpcli_context);
+}
+
+grpc_credentials_status
+grpc_oauth2_token_fetcher_credentials_parse_server_response(
+    const grpc_http_response *response, grpc_credentials_md_store **token_md,
+    gpr_timespec *token_lifetime) {
+  char *null_terminated_body = NULL;
+  char *new_access_token = NULL;
+  grpc_credentials_status status = GRPC_CREDENTIALS_OK;
+  grpc_json *json = NULL;
+
+  if (response == NULL) {
+    gpr_log(GPR_ERROR, "Received NULL response.");
+    status = GRPC_CREDENTIALS_ERROR;
+    goto end;
+  }
+
+  if (response->body_length > 0) {
+    null_terminated_body = gpr_malloc(response->body_length + 1);
+    null_terminated_body[response->body_length] = '\0';
+    memcpy(null_terminated_body, response->body, response->body_length);
+  }
+
+  if (response->status != 200) {
+    gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].",
+            response->status,
+            null_terminated_body != NULL ? null_terminated_body : "");
+    status = GRPC_CREDENTIALS_ERROR;
+    goto end;
+  } else {
+    grpc_json *access_token = NULL;
+    grpc_json *token_type = NULL;
+    grpc_json *expires_in = NULL;
+    grpc_json *ptr;
+    json = grpc_json_parse_string(null_terminated_body);
+    if (json == NULL) {
+      gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body);
+      status = GRPC_CREDENTIALS_ERROR;
+      goto end;
+    }
+    if (json->type != GRPC_JSON_OBJECT) {
+      gpr_log(GPR_ERROR, "Response should be a JSON object");
+      status = GRPC_CREDENTIALS_ERROR;
+      goto end;
+    }
+    for (ptr = json->child; ptr; ptr = ptr->next) {
+      if (strcmp(ptr->key, "access_token") == 0) {
+        access_token = ptr;
+      } else if (strcmp(ptr->key, "token_type") == 0) {
+        token_type = ptr;
+      } else if (strcmp(ptr->key, "expires_in") == 0) {
+        expires_in = ptr;
+      }
+    }
+    if (access_token == NULL || access_token->type != GRPC_JSON_STRING) {
+      gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON.");
+      status = GRPC_CREDENTIALS_ERROR;
+      goto end;
+    }
+    if (token_type == NULL || token_type->type != GRPC_JSON_STRING) {
+      gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON.");
+      status = GRPC_CREDENTIALS_ERROR;
+      goto end;
+    }
+    if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) {
+      gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON.");
+      status = GRPC_CREDENTIALS_ERROR;
+      goto end;
+    }
+    gpr_asprintf(&new_access_token, "%s %s", token_type->value,
+                 access_token->value);
+    token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10);
+    token_lifetime->tv_nsec = 0;
+    token_lifetime->clock_type = GPR_TIMESPAN;
+    if (*token_md != NULL) grpc_credentials_md_store_unref(*token_md);
+    *token_md = grpc_credentials_md_store_create(1);
+    grpc_credentials_md_store_add_cstrings(
+        *token_md, GRPC_AUTHORIZATION_METADATA_KEY, new_access_token);
+    status = GRPC_CREDENTIALS_OK;
+  }
+
+end:
+  if (status != GRPC_CREDENTIALS_OK && (*token_md != NULL)) {
+    grpc_credentials_md_store_unref(*token_md);
+    *token_md = NULL;
+  }
+  if (null_terminated_body != NULL) gpr_free(null_terminated_body);
+  if (new_access_token != NULL) gpr_free(new_access_token);
+  if (json != NULL) grpc_json_destroy(json);
+  return status;
+}
+
+static void on_oauth2_token_fetcher_http_response(grpc_exec_ctx *exec_ctx,
+                                                  void *user_data,
+                                                  grpc_error *error) {
+  grpc_credentials_metadata_request *r =
+      (grpc_credentials_metadata_request *)user_data;
+  grpc_oauth2_token_fetcher_credentials *c =
+      (grpc_oauth2_token_fetcher_credentials *)r->creds;
+  gpr_timespec token_lifetime;
+  grpc_credentials_status status;
+
+  gpr_mu_lock(&c->mu);
+  status = grpc_oauth2_token_fetcher_credentials_parse_server_response(
+      &r->response, &c->access_token_md, &token_lifetime);
+  if (status == GRPC_CREDENTIALS_OK) {
+    c->token_expiration =
+        gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime);
+    r->cb(exec_ctx, r->user_data, c->access_token_md->entries,
+          c->access_token_md->num_entries, status);
+  } else {
+    c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
+    r->cb(exec_ctx, r->user_data, NULL, 0, status);
+  }
+  gpr_mu_unlock(&c->mu);
+  grpc_credentials_metadata_request_destroy(r);
+}
+
+static void oauth2_token_fetcher_get_request_metadata(
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
+    grpc_pollset *pollset, grpc_auth_metadata_context context,
+    grpc_credentials_metadata_cb cb, void *user_data) {
+  grpc_oauth2_token_fetcher_credentials *c =
+      (grpc_oauth2_token_fetcher_credentials *)creds;
+  gpr_timespec refresh_threshold = gpr_time_from_seconds(
+      GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN);
+  grpc_credentials_md_store *cached_access_token_md = NULL;
+  {
+    gpr_mu_lock(&c->mu);
+    if (c->access_token_md != NULL &&
+        (gpr_time_cmp(
+             gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)),
+             refresh_threshold) > 0)) {
+      cached_access_token_md =
+          grpc_credentials_md_store_ref(c->access_token_md);
+    }
+    gpr_mu_unlock(&c->mu);
+  }
+  if (cached_access_token_md != NULL) {
+    cb(exec_ctx, user_data, cached_access_token_md->entries,
+       cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK);
+    grpc_credentials_md_store_unref(cached_access_token_md);
+  } else {
+    c->fetch_func(
+        exec_ctx,
+        grpc_credentials_metadata_request_create(creds, cb, user_data),
+        &c->httpcli_context, pollset, on_oauth2_token_fetcher_http_response,
+        gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), refresh_threshold));
+  }
+}
+
+static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c,
+                                      grpc_fetch_oauth2_func fetch_func) {
+  memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials));
+  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
+  gpr_ref_init(&c->base.refcount, 1);
+  gpr_mu_init(&c->mu);
+  c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME);
+  c->fetch_func = fetch_func;
+  grpc_httpcli_context_init(&c->httpcli_context);
+}
+
+//
+//  Google Compute Engine credentials.
+//
+
+static grpc_call_credentials_vtable compute_engine_vtable = {
+    oauth2_token_fetcher_destruct, oauth2_token_fetcher_get_request_metadata};
+
+static void compute_engine_fetch_oauth2(
+    grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
+    grpc_httpcli_context *httpcli_context, grpc_pollset *pollset,
+    grpc_iomgr_cb_func response_cb, gpr_timespec deadline) {
+  grpc_http_header header = {"Metadata-Flavor", "Google"};
+  grpc_httpcli_request request;
+  memset(&request, 0, sizeof(grpc_httpcli_request));
+  request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST;
+  request.http.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
+  request.http.hdr_count = 1;
+  request.http.hdrs = &header;
+  grpc_httpcli_get(exec_ctx, httpcli_context, pollset, &request, deadline,
+                   grpc_closure_create(response_cb, metadata_req),
+                   &metadata_req->response);
+}
+
+grpc_call_credentials *grpc_google_compute_engine_credentials_create(
+    void *reserved) {
+  grpc_oauth2_token_fetcher_credentials *c =
+      gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials));
+  GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1,
+                 (reserved));
+  GPR_ASSERT(reserved == NULL);
+  init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2);
+  c->base.vtable = &compute_engine_vtable;
+  return &c->base;
+}
+
+//
+// Google Refresh Token credentials.
+//
+
+static void refresh_token_destruct(grpc_call_credentials *creds) {
+  grpc_google_refresh_token_credentials *c =
+      (grpc_google_refresh_token_credentials *)creds;
+  grpc_auth_refresh_token_destruct(&c->refresh_token);
+  oauth2_token_fetcher_destruct(&c->base.base);
+}
+
+static grpc_call_credentials_vtable refresh_token_vtable = {
+    refresh_token_destruct, oauth2_token_fetcher_get_request_metadata};
+
+static void refresh_token_fetch_oauth2(
+    grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req,
+    grpc_httpcli_context *httpcli_context, grpc_pollset *pollset,
+    grpc_iomgr_cb_func response_cb, gpr_timespec deadline) {
+  grpc_google_refresh_token_credentials *c =
+      (grpc_google_refresh_token_credentials *)metadata_req->creds;
+  grpc_http_header header = {"Content-Type",
+                             "application/x-www-form-urlencoded"};
+  grpc_httpcli_request request;
+  char *body = NULL;
+  gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING,
+               c->refresh_token.client_id, c->refresh_token.client_secret,
+               c->refresh_token.refresh_token);
+  memset(&request, 0, sizeof(grpc_httpcli_request));
+  request.host = GRPC_GOOGLE_OAUTH2_SERVICE_HOST;
+  request.http.path = GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
+  request.http.hdr_count = 1;
+  request.http.hdrs = &header;
+  request.handshaker = &grpc_httpcli_ssl;
+  grpc_httpcli_post(exec_ctx, httpcli_context, pollset, &request, body,
+                    strlen(body), deadline,
+                    grpc_closure_create(response_cb, metadata_req),
+                    &metadata_req->response);
+  gpr_free(body);
+}
+
+grpc_call_credentials *
+grpc_refresh_token_credentials_create_from_auth_refresh_token(
+    grpc_auth_refresh_token refresh_token) {
+  grpc_google_refresh_token_credentials *c;
+  if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
+    gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation");
+    return NULL;
+  }
+  c = gpr_malloc(sizeof(grpc_google_refresh_token_credentials));
+  memset(c, 0, sizeof(grpc_google_refresh_token_credentials));
+  init_oauth2_token_fetcher(&c->base, refresh_token_fetch_oauth2);
+  c->base.base.vtable = &refresh_token_vtable;
+  c->refresh_token = refresh_token;
+  return &c->base.base;
+}
+
+grpc_call_credentials *grpc_google_refresh_token_credentials_create(
+    const char *json_refresh_token, void *reserved) {
+  GRPC_API_TRACE(
+      "grpc_refresh_token_credentials_create(json_refresh_token=%s, "
+      "reserved=%p)",
+      2, (json_refresh_token, reserved));
+  GPR_ASSERT(reserved == NULL);
+  return grpc_refresh_token_credentials_create_from_auth_refresh_token(
+      grpc_auth_refresh_token_create_from_string(json_refresh_token));
+}
+
+//
+// Oauth2 Access Token credentials.
+//
+
+static void access_token_destruct(grpc_call_credentials *creds) {
+  grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
+  grpc_credentials_md_store_unref(c->access_token_md);
+}
+
+static void access_token_get_request_metadata(
+    grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds,
+    grpc_pollset *pollset, grpc_auth_metadata_context context,
+    grpc_credentials_metadata_cb cb, void *user_data) {
+  grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
+  cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK);
+}
+
+static grpc_call_credentials_vtable access_token_vtable = {
+    access_token_destruct, access_token_get_request_metadata};
+
+grpc_call_credentials *grpc_access_token_credentials_create(
+    const char *access_token, void *reserved) {
+  grpc_access_token_credentials *c =
+      gpr_malloc(sizeof(grpc_access_token_credentials));
+  char *token_md_value;
+  GRPC_API_TRACE(
+      "grpc_access_token_credentials_create(access_token=%s, "
+      "reserved=%p)",
+      2, (access_token, reserved));
+  GPR_ASSERT(reserved == NULL);
+  memset(c, 0, sizeof(grpc_access_token_credentials));
+  c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2;
+  c->base.vtable = &access_token_vtable;
+  gpr_ref_init(&c->base.refcount, 1);
+  c->access_token_md = grpc_credentials_md_store_create(1);
+  gpr_asprintf(&token_md_value, "Bearer %s", access_token);
+  grpc_credentials_md_store_add_cstrings(
+      c->access_token_md, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value);
+  gpr_free(token_md_value);
+  return &c->base;
+}
diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
new file mode 100644
index 0000000..017f823
--- /dev/null
+++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h
@@ -0,0 +1,109 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_CREDENTIALS_H
+
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/security/credentials/credentials.h"
+
+// auth_refresh_token parsing.
+typedef struct {
+  const char *type;
+  char *client_id;
+  char *client_secret;
+  char *refresh_token;
+} grpc_auth_refresh_token;
+
+/// Returns 1 if the object is valid, 0 otherwise.
+int grpc_auth_refresh_token_is_valid(
+    const grpc_auth_refresh_token *refresh_token);
+
+/// Creates a refresh token object from string. Returns an invalid object if a
+/// parsing error has been encountered.
+grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
+    const char *json_string);
+
+/// Creates a refresh token object from parsed json. Returns an invalid object
+/// if a parsing error has been encountered.
+grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
+    const grpc_json *json);
+
+/// Destructs the object.
+void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token);
+
+// -- Oauth2 Token Fetcher credentials --
+//
+//  This object is a base for credentials that need to acquire an oauth2 token
+//  from an http service.
+
+typedef void (*grpc_fetch_oauth2_func)(grpc_exec_ctx *exec_ctx,
+                                       grpc_credentials_metadata_request *req,
+                                       grpc_httpcli_context *http_context,
+                                       grpc_pollset *pollset,
+                                       grpc_iomgr_cb_func cb,
+                                       gpr_timespec deadline);
+typedef struct {
+  grpc_call_credentials base;
+  gpr_mu mu;
+  grpc_credentials_md_store *access_token_md;
+  gpr_timespec token_expiration;
+  grpc_httpcli_context httpcli_context;
+  grpc_fetch_oauth2_func fetch_func;
+} grpc_oauth2_token_fetcher_credentials;
+
+// Google refresh token credentials.
+typedef struct {
+  grpc_oauth2_token_fetcher_credentials base;
+  grpc_auth_refresh_token refresh_token;
+} grpc_google_refresh_token_credentials;
+
+// Access token credentials.
+typedef struct {
+  grpc_call_credentials base;
+  grpc_credentials_md_store *access_token_md;
+} grpc_access_token_credentials;
+
+// Private constructor for refresh token credentials from an already parsed
+// refresh token. Takes ownership of the refresh token.
+grpc_call_credentials *
+grpc_refresh_token_credentials_create_from_auth_refresh_token(
+    grpc_auth_refresh_token token);
+
+// Exposed for testing only.
+grpc_credentials_status
+grpc_oauth2_token_fetcher_credentials_parse_server_response(
+    const struct grpc_http_response *response,
+    grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime);
+
+#endif  // GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_CREDENTIALS_H
diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.c b/src/core/lib/security/credentials/plugin/plugin_credentials.c
new file mode 100644
index 0000000..bae3573
--- /dev/null
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.c
@@ -0,0 +1,129 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/security/credentials/plugin/plugin_credentials.h"
+
+#include <string.h>
+
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+
+typedef struct {
+  void *user_data;
+  grpc_credentials_metadata_cb cb;
+} grpc_metadata_plugin_request;
+
+static void plugin_destruct(grpc_call_credentials *creds) {
+  grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds;
+  if (c->plugin.state != NULL && c->plugin.destroy != NULL) {
+    c->plugin.destroy(c->plugin.state);
+  }
+}
+
+static void plugin_md_request_metadata_ready(void *request,
+                                             const grpc_metadata *md,
+                                             size_t num_md,
+                                             grpc_status_code status,
+                                             const char *error_details) {
+  /* called from application code */
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_metadata_plugin_request *r = (grpc_metadata_plugin_request *)request;
+  if (status != GRPC_STATUS_OK) {
+    if (error_details != NULL) {
+      gpr_log(GPR_ERROR, "Getting metadata from plugin failed with error: %s",
+              error_details);
+    }
+    r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR);
+  } else {
+    size_t i;
+    grpc_credentials_md *md_array = NULL;
+    if (num_md > 0) {
+      md_array = gpr_malloc(num_md * sizeof(grpc_credentials_md));
+      for (i = 0; i < num_md; i++) {
+        md_array[i].key = gpr_slice_from_copied_string(md[i].key);
+        md_array[i].value =
+            gpr_slice_from_copied_buffer(md[i].value, md[i].value_length);
+      }
+    }
+    r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK);
+    if (md_array != NULL) {
+      for (i = 0; i < num_md; i++) {
+        gpr_slice_unref(md_array[i].key);
+        gpr_slice_unref(md_array[i].value);
+      }
+      gpr_free(md_array);
+    }
+  }
+  gpr_free(r);
+  grpc_exec_ctx_finish(&exec_ctx);
+}
+
+static void plugin_get_request_metadata(grpc_exec_ctx *exec_ctx,
+                                        grpc_call_credentials *creds,
+                                        grpc_pollset *pollset,
+                                        grpc_auth_metadata_context context,
+                                        grpc_credentials_metadata_cb cb,
+                                        void *user_data) {
+  grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds;
+  if (c->plugin.get_metadata != NULL) {
+    grpc_metadata_plugin_request *request = gpr_malloc(sizeof(*request));
+    memset(request, 0, sizeof(*request));
+    request->user_data = user_data;
+    request->cb = cb;
+    c->plugin.get_metadata(c->plugin.state, context,
+                           plugin_md_request_metadata_ready, request);
+  } else {
+    cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK);
+  }
+}
+
+static grpc_call_credentials_vtable plugin_vtable = {
+    plugin_destruct, plugin_get_request_metadata};
+
+grpc_call_credentials *grpc_metadata_credentials_create_from_plugin(
+    grpc_metadata_credentials_plugin plugin, void *reserved) {
+  grpc_plugin_credentials *c = gpr_malloc(sizeof(*c));
+  GRPC_API_TRACE("grpc_metadata_credentials_create_from_plugin(reserved=%p)", 1,
+                 (reserved));
+  GPR_ASSERT(reserved == NULL);
+  memset(c, 0, sizeof(*c));
+  c->base.type = plugin.type;
+  c->base.vtable = &plugin_vtable;
+  gpr_ref_init(&c->base.refcount, 1);
+  c->plugin = plugin;
+  return &c->base;
+}
diff --git a/src/core/lib/security/auth_filters.h b/src/core/lib/security/credentials/plugin/plugin_credentials.h
similarity index 77%
copy from src/core/lib/security/auth_filters.h
copy to src/core/lib/security/credentials/plugin/plugin_credentials.h
index 7fb56c3..0b91d2f 100644
--- a/src/core/lib/security/auth_filters.h
+++ b/src/core/lib/security/credentials/plugin/plugin_credentials.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,12 +31,15 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
-#define GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_CREDENTIALS_H
 
-#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/security/credentials/credentials.h"
 
-extern const grpc_channel_filter grpc_client_auth_filter;
-extern const grpc_channel_filter grpc_server_auth_filter;
+typedef struct {
+  grpc_call_credentials base;
+  grpc_metadata_credentials_plugin plugin;
+  grpc_credentials_md_store *plugin_md;
+} grpc_plugin_credentials;
 
-#endif /* GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H */
+#endif  // GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_CREDENTIALS_H
diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.c b/src/core/lib/security/credentials/ssl/ssl_credentials.c
new file mode 100644
index 0000000..545bca9
--- /dev/null
+++ b/src/core/lib/security/credentials/ssl/ssl_credentials.c
@@ -0,0 +1,240 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/lib/security/credentials/ssl/ssl_credentials.h"
+
+#include <string.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/http_client_filter.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+//
+// Utils
+//
+
+static void ssl_copy_key_material(const char *input, unsigned char **output,
+                                  size_t *output_size) {
+  *output_size = strlen(input);
+  *output = gpr_malloc(*output_size);
+  memcpy(*output, input, *output_size);
+}
+
+//
+// SSL Channel Credentials.
+//
+
+static void ssl_destruct(grpc_channel_credentials *creds) {
+  grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
+  if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
+  if (c->config.pem_private_key != NULL) gpr_free(c->config.pem_private_key);
+  if (c->config.pem_cert_chain != NULL) gpr_free(c->config.pem_cert_chain);
+}
+
+static grpc_security_status ssl_create_security_connector(
+    grpc_channel_credentials *creds, grpc_call_credentials *call_creds,
+    const char *target, const grpc_channel_args *args,
+    grpc_channel_security_connector **sc, grpc_channel_args **new_args) {
+  grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds;
+  grpc_security_status status = GRPC_SECURITY_OK;
+  size_t i = 0;
+  const char *overridden_target_name = NULL;
+  grpc_arg new_arg;
+
+  for (i = 0; args && i < args->num_args; i++) {
+    grpc_arg *arg = &args->args[i];
+    if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 &&
+        arg->type == GRPC_ARG_STRING) {
+      overridden_target_name = arg->value.string;
+      break;
+    }
+  }
+  status = grpc_ssl_channel_security_connector_create(
+      call_creds, &c->config, target, overridden_target_name, sc);
+  if (status != GRPC_SECURITY_OK) {
+    return status;
+  }
+  new_arg.type = GRPC_ARG_STRING;
+  new_arg.key = GRPC_ARG_HTTP2_SCHEME;
+  new_arg.value.string = "https";
+  *new_args = grpc_channel_args_copy_and_add(args, &new_arg, 1);
+  return status;
+}
+
+static grpc_channel_credentials_vtable ssl_vtable = {
+    ssl_destruct, ssl_create_security_connector};
+
+static void ssl_build_config(const char *pem_root_certs,
+                             grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
+                             grpc_ssl_config *config) {
+  if (pem_root_certs != NULL) {
+    ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
+                          &config->pem_root_certs_size);
+  }
+  if (pem_key_cert_pair != NULL) {
+    GPR_ASSERT(pem_key_cert_pair->private_key != NULL);
+    GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL);
+    ssl_copy_key_material(pem_key_cert_pair->private_key,
+                          &config->pem_private_key,
+                          &config->pem_private_key_size);
+    ssl_copy_key_material(pem_key_cert_pair->cert_chain,
+                          &config->pem_cert_chain,
+                          &config->pem_cert_chain_size);
+  }
+}
+
+grpc_channel_credentials *grpc_ssl_credentials_create(
+    const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair,
+    void *reserved) {
+  grpc_ssl_credentials *c = gpr_malloc(sizeof(grpc_ssl_credentials));
+  GRPC_API_TRACE(
+      "grpc_ssl_credentials_create(pem_root_certs=%s, "
+      "pem_key_cert_pair=%p, "
+      "reserved=%p)",
+      3, (pem_root_certs, pem_key_cert_pair, reserved));
+  GPR_ASSERT(reserved == NULL);
+  memset(c, 0, sizeof(grpc_ssl_credentials));
+  c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL;
+  c->base.vtable = &ssl_vtable;
+  gpr_ref_init(&c->base.refcount, 1);
+  ssl_build_config(pem_root_certs, pem_key_cert_pair, &c->config);
+  return &c->base;
+}
+
+//
+// SSL Server Credentials.
+//
+
+static void ssl_server_destruct(grpc_server_credentials *creds) {
+  grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
+  size_t i;
+  for (i = 0; i < c->config.num_key_cert_pairs; i++) {
+    if (c->config.pem_private_keys[i] != NULL) {
+      gpr_free(c->config.pem_private_keys[i]);
+    }
+    if (c->config.pem_cert_chains[i] != NULL) {
+      gpr_free(c->config.pem_cert_chains[i]);
+    }
+  }
+  if (c->config.pem_private_keys != NULL) gpr_free(c->config.pem_private_keys);
+  if (c->config.pem_private_keys_sizes != NULL) {
+    gpr_free(c->config.pem_private_keys_sizes);
+  }
+  if (c->config.pem_cert_chains != NULL) gpr_free(c->config.pem_cert_chains);
+  if (c->config.pem_cert_chains_sizes != NULL) {
+    gpr_free(c->config.pem_cert_chains_sizes);
+  }
+  if (c->config.pem_root_certs != NULL) gpr_free(c->config.pem_root_certs);
+}
+
+static grpc_security_status ssl_server_create_security_connector(
+    grpc_server_credentials *creds, grpc_server_security_connector **sc) {
+  grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds;
+  return grpc_ssl_server_security_connector_create(&c->config, sc);
+}
+
+static grpc_server_credentials_vtable ssl_server_vtable = {
+    ssl_server_destruct, ssl_server_create_security_connector};
+
+static void ssl_build_server_config(
+    const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
+    size_t num_key_cert_pairs,
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    grpc_ssl_server_config *config) {
+  size_t i;
+  config->client_certificate_request = client_certificate_request;
+  if (pem_root_certs != NULL) {
+    ssl_copy_key_material(pem_root_certs, &config->pem_root_certs,
+                          &config->pem_root_certs_size);
+  }
+  if (num_key_cert_pairs > 0) {
+    GPR_ASSERT(pem_key_cert_pairs != NULL);
+    config->pem_private_keys =
+        gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
+    config->pem_cert_chains =
+        gpr_malloc(num_key_cert_pairs * sizeof(unsigned char *));
+    config->pem_private_keys_sizes =
+        gpr_malloc(num_key_cert_pairs * sizeof(size_t));
+    config->pem_cert_chains_sizes =
+        gpr_malloc(num_key_cert_pairs * sizeof(size_t));
+  }
+  config->num_key_cert_pairs = num_key_cert_pairs;
+  for (i = 0; i < num_key_cert_pairs; i++) {
+    GPR_ASSERT(pem_key_cert_pairs[i].private_key != NULL);
+    GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != NULL);
+    ssl_copy_key_material(pem_key_cert_pairs[i].private_key,
+                          &config->pem_private_keys[i],
+                          &config->pem_private_keys_sizes[i]);
+    ssl_copy_key_material(pem_key_cert_pairs[i].cert_chain,
+                          &config->pem_cert_chains[i],
+                          &config->pem_cert_chains_sizes[i]);
+  }
+}
+
+grpc_server_credentials *grpc_ssl_server_credentials_create(
+    const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
+    size_t num_key_cert_pairs, int force_client_auth, void *reserved) {
+  return grpc_ssl_server_credentials_create_ex(
+      pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs,
+      force_client_auth
+          ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
+          : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
+      reserved);
+}
+
+grpc_server_credentials *grpc_ssl_server_credentials_create_ex(
+    const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs,
+    size_t num_key_cert_pairs,
+    grpc_ssl_client_certificate_request_type client_certificate_request,
+    void *reserved) {
+  grpc_ssl_server_credentials *c =
+      gpr_malloc(sizeof(grpc_ssl_server_credentials));
+  GRPC_API_TRACE(
+      "grpc_ssl_server_credentials_create_ex("
+      "pem_root_certs=%s, pem_key_cert_pairs=%p, num_key_cert_pairs=%lu, "
+      "client_certificate_request=%d, reserved=%p)",
+      5, (pem_root_certs, pem_key_cert_pairs, (unsigned long)num_key_cert_pairs,
+          client_certificate_request, reserved));
+  GPR_ASSERT(reserved == NULL);
+  memset(c, 0, sizeof(grpc_ssl_server_credentials));
+  c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL;
+  gpr_ref_init(&c->base.refcount, 1);
+  c->base.vtable = &ssl_server_vtable;
+  ssl_build_server_config(pem_root_certs, pem_key_cert_pairs,
+                          num_key_cert_pairs, client_certificate_request,
+                          &c->config);
+  return &c->base;
+}
diff --git a/src/core/lib/security/auth_filters.h b/src/core/lib/security/credentials/ssl/ssl_credentials.h
similarity index 74%
copy from src/core/lib/security/auth_filters.h
copy to src/core/lib/security/credentials/ssl/ssl_credentials.h
index 7fb56c3..f23dbdb 100644
--- a/src/core/lib/security/auth_filters.h
+++ b/src/core/lib/security/credentials/ssl/ssl_credentials.h
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -30,13 +30,19 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
+#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H
 
-#ifndef GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
-#define GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
+#include "src/core/lib/security/credentials/credentials.h"
 
-#include "src/core/lib/channel/channel_stack.h"
+typedef struct {
+  grpc_channel_credentials base;
+  grpc_ssl_config config;
+} grpc_ssl_credentials;
 
-extern const grpc_channel_filter grpc_client_auth_filter;
-extern const grpc_channel_filter grpc_server_auth_filter;
+typedef struct {
+  grpc_server_credentials base;
+  grpc_ssl_server_config config;
+} grpc_ssl_server_credentials;
 
-#endif /* GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H */
+#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H */
diff --git a/src/core/lib/security/auth_filters.h b/src/core/lib/security/transport/auth_filters.h
similarity index 90%
rename from src/core/lib/security/auth_filters.h
rename to src/core/lib/security/transport/auth_filters.h
index 7fb56c3..f688d4e 100644
--- a/src/core/lib/security/auth_filters.h
+++ b/src/core/lib/security/transport/auth_filters.h
@@ -31,12 +31,12 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
-#define GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
+#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H
 
 #include "src/core/lib/channel/channel_stack.h"
 
 extern const grpc_channel_filter grpc_client_auth_filter;
 extern const grpc_channel_filter grpc_server_auth_filter;
 
-#endif /* GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H */
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H */
diff --git a/src/core/lib/security/client_auth_filter.c b/src/core/lib/security/transport/client_auth_filter.c
similarity index 97%
rename from src/core/lib/security/client_auth_filter.c
rename to src/core/lib/security/transport/client_auth_filter.c
index 8b58cb8..e3cbcb4 100644
--- a/src/core/lib/security/client_auth_filter.c
+++ b/src/core/lib/security/transport/client_auth_filter.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/lib/security/auth_filters.h"
+#include "src/core/lib/security/transport/auth_filters.h"
 
 #include <string.h>
 
@@ -40,9 +40,9 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/lib/channel/channel_stack.h"
-#include "src/core/lib/security/credentials.h"
-#include "src/core/lib/security/security_connector.h"
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/transport/security_connector.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/surface/call.h"
 #include "src/core/lib/transport/static_metadata.h"
diff --git a/src/core/lib/security/handshake.c b/src/core/lib/security/transport/handshake.c
similarity index 84%
rename from src/core/lib/security/handshake.c
rename to src/core/lib/security/transport/handshake.c
index d5fe0c7..c87262f 100644
--- a/src/core/lib/security/handshake.c
+++ b/src/core/lib/security/transport/handshake.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/lib/security/handshake.h"
+#include "src/core/lib/security/transport/handshake.h"
 
 #include <stdbool.h>
 #include <string.h>
@@ -39,8 +39,9 @@
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/slice_buffer.h>
-#include "src/core/lib/security/secure_endpoint.h"
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/transport/secure_endpoint.h"
+#include "src/core/lib/security/transport/tsi_error.h"
 
 #define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256
 
@@ -63,10 +64,11 @@
 } grpc_security_handshake;
 
 static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
-                                                 void *setup, bool success);
+                                                 void *setup,
+                                                 grpc_error *error);
 
 static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *setup,
-                                           bool success);
+                                           grpc_error *error);
 
 static void security_connector_remove_handshake(grpc_security_handshake *h) {
   GPR_ASSERT(!h->is_client_side);
@@ -97,14 +99,18 @@
 
 static void security_handshake_done(grpc_exec_ctx *exec_ctx,
                                     grpc_security_handshake *h,
-                                    int is_success) {
+                                    grpc_error *error) {
   if (!h->is_client_side) {
     security_connector_remove_handshake(h);
   }
-  if (is_success) {
+  if (error == GRPC_ERROR_NONE) {
     h->cb(exec_ctx, h->user_data, GRPC_SECURITY_OK, h->secure_endpoint,
           h->auth_context);
   } else {
+    const char *msg = grpc_error_string(error);
+    gpr_log(GPR_ERROR, "Security handshake failed: %s", msg);
+    grpc_error_free_string(msg);
+
     if (h->secure_endpoint != NULL) {
       grpc_endpoint_shutdown(exec_ctx, h->secure_endpoint);
       grpc_endpoint_destroy(exec_ctx, h->secure_endpoint);
@@ -121,6 +127,7 @@
   GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake");
   GRPC_SECURITY_CONNECTOR_UNREF(h->connector, "handshake");
   gpr_free(h);
+  GRPC_ERROR_UNREF(error);
 }
 
 static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *user_data,
@@ -130,17 +137,20 @@
   tsi_frame_protector *protector;
   tsi_result result;
   if (status != GRPC_SECURITY_OK) {
-    gpr_log(GPR_ERROR, "Error checking peer.");
-    security_handshake_done(exec_ctx, h, 0);
+    security_handshake_done(
+        exec_ctx, h,
+        grpc_error_set_int(GRPC_ERROR_CREATE("Error checking peer."),
+                           GRPC_ERROR_INT_SECURITY_STATUS, status));
     return;
   }
   h->auth_context = GRPC_AUTH_CONTEXT_REF(auth_context, "handshake");
   result =
       tsi_handshaker_create_frame_protector(h->handshaker, NULL, &protector);
   if (result != TSI_OK) {
-    gpr_log(GPR_ERROR, "Frame protector creation failed with error %s.",
-            tsi_result_to_string(result));
-    security_handshake_done(exec_ctx, h, 0);
+    security_handshake_done(
+        exec_ctx, h,
+        grpc_set_tsi_error_bits(
+            GRPC_ERROR_CREATE("Frame protector creation failed"), result));
     return;
   }
   h->secure_endpoint =
@@ -148,7 +158,7 @@
                                   h->left_overs.slices, h->left_overs.count);
   h->left_overs.count = 0;
   h->left_overs.length = 0;
-  security_handshake_done(exec_ctx, h, 1);
+  security_handshake_done(exec_ctx, h, GRPC_ERROR_NONE);
   return;
 }
 
@@ -157,9 +167,9 @@
   tsi_result result = tsi_handshaker_extract_peer(h->handshaker, &peer);
 
   if (result != TSI_OK) {
-    gpr_log(GPR_ERROR, "Peer extraction failed with error %s",
-            tsi_result_to_string(result));
-    security_handshake_done(exec_ctx, h, 0);
+    security_handshake_done(
+        exec_ctx, h, grpc_set_tsi_error_bits(
+                         GRPC_ERROR_CREATE("Peer extraction failed"), result));
     return;
   }
   grpc_security_connector_check_peer(exec_ctx, h->connector, peer,
@@ -185,9 +195,9 @@
   } while (result == TSI_INCOMPLETE_DATA);
 
   if (result != TSI_OK) {
-    gpr_log(GPR_ERROR, "Handshake failed with error %s",
-            tsi_result_to_string(result));
-    security_handshake_done(exec_ctx, h, 0);
+    security_handshake_done(
+        exec_ctx, h,
+        grpc_set_tsi_error_bits(GRPC_ERROR_CREATE("Handshake failed"), result));
     return;
   }
 
@@ -203,7 +213,7 @@
 
 static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
                                                  void *handshake,
-                                                 bool success) {
+                                                 grpc_error *error) {
   grpc_security_handshake *h = handshake;
   size_t consumed_slice_size = 0;
   tsi_result result = TSI_OK;
@@ -211,9 +221,10 @@
   size_t num_left_overs;
   int has_left_overs_in_current_slice = 0;
 
-  if (!success) {
-    gpr_log(GPR_ERROR, "Read failed.");
-    security_handshake_done(exec_ctx, h, 0);
+  if (error != GRPC_ERROR_NONE) {
+    security_handshake_done(
+        exec_ctx, h,
+        GRPC_ERROR_CREATE_REFERENCING("Handshake read failed", &error, 1));
     return;
   }
 
@@ -238,9 +249,9 @@
   }
 
   if (result != TSI_OK) {
-    gpr_log(GPR_ERROR, "Handshake failed with error %s",
-            tsi_result_to_string(result));
-    security_handshake_done(exec_ctx, h, 0);
+    security_handshake_done(
+        exec_ctx, h,
+        grpc_set_tsi_error_bits(GRPC_ERROR_CREATE("Handshake failed"), result));
     return;
   }
 
@@ -270,13 +281,15 @@
 
 /* If handshake is NULL, the handshake is done. */
 static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx,
-                                           void *handshake, bool success) {
+                                           void *handshake, grpc_error *error) {
   grpc_security_handshake *h = handshake;
 
   /* Make sure that write is OK. */
-  if (!success) {
-    gpr_log(GPR_ERROR, "Write failed.");
-    if (handshake != NULL) security_handshake_done(exec_ctx, h, 0);
+  if (error != GRPC_ERROR_NONE) {
+    if (handshake != NULL)
+      security_handshake_done(
+          exec_ctx, h,
+          GRPC_ERROR_CREATE_REFERENCING("Handshake write failed", &error, 1));
     return;
   }
 
diff --git a/src/core/lib/security/handshake.h b/src/core/lib/security/transport/handshake.h
similarity index 90%
rename from src/core/lib/security/handshake.h
rename to src/core/lib/security/transport/handshake.h
index f34476e..6ed850b 100644
--- a/src/core/lib/security/handshake.h
+++ b/src/core/lib/security/transport/handshake.h
@@ -31,11 +31,11 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_HANDSHAKE_H
-#define GRPC_CORE_LIB_SECURITY_HANDSHAKE_H
+#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_HANDSHAKE_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_HANDSHAKE_H
 
 #include "src/core/lib/iomgr/endpoint.h"
-#include "src/core/lib/security/security_connector.h"
+#include "src/core/lib/security/transport/security_connector.h"
 
 /* Calls the callback upon completion. Takes owership of handshaker. */
 void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx,
@@ -48,4 +48,4 @@
 
 void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx, void *handshake);
 
-#endif /* GRPC_CORE_LIB_SECURITY_HANDSHAKE_H */
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_HANDSHAKE_H */
diff --git a/src/core/lib/security/secure_endpoint.c b/src/core/lib/security/transport/secure_endpoint.c
similarity index 94%
rename from src/core/lib/security/secure_endpoint.c
rename to src/core/lib/security/transport/secure_endpoint.c
index 27b0e98..23b51f3 100644
--- a/src/core/lib/security/secure_endpoint.c
+++ b/src/core/lib/security/transport/secure_endpoint.c
@@ -31,13 +31,14 @@
  *
  */
 
-#include "src/core/lib/security/secure_endpoint.h"
+#include "src/core/lib/security/transport/secure_endpoint.h"
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/slice.h>
 #include <grpc/support/slice_buffer.h>
 #include <grpc/support/sync.h>
 #include "src/core/lib/debug/trace.h"
+#include "src/core/lib/security/transport/tsi_error.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/tsi/transport_security_interface.h"
 
@@ -126,7 +127,7 @@
 }
 
 static void call_read_cb(grpc_exec_ctx *exec_ctx, secure_endpoint *ep,
-                         bool success) {
+                         grpc_error *error) {
   if (grpc_trace_secure_endpoint) {
     size_t i;
     for (i = 0; i < ep->read_buffer->count; i++) {
@@ -137,11 +138,12 @@
     }
   }
   ep->read_buffer = NULL;
-  grpc_exec_ctx_enqueue(exec_ctx, ep->read_cb, success, NULL);
+  grpc_exec_ctx_push(exec_ctx, ep->read_cb, error, NULL);
   SECURE_ENDPOINT_UNREF(exec_ctx, ep, "read");
 }
 
-static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
+static void on_read(grpc_exec_ctx *exec_ctx, void *user_data,
+                    grpc_error *error) {
   unsigned i;
   uint8_t keep_looping = 0;
   tsi_result result = TSI_OK;
@@ -149,9 +151,10 @@
   uint8_t *cur = GPR_SLICE_START_PTR(ep->read_staging_buffer);
   uint8_t *end = GPR_SLICE_END_PTR(ep->read_staging_buffer);
 
-  if (!success) {
+  if (error != GRPC_ERROR_NONE) {
     gpr_slice_buffer_reset_and_unref(ep->read_buffer);
-    call_read_cb(exec_ctx, ep, 0);
+    call_read_cb(exec_ctx, ep, GRPC_ERROR_CREATE_REFERENCING(
+                                   "Secure read failed", &error, 1));
     return;
   }
 
@@ -208,11 +211,12 @@
 
   if (result != TSI_OK) {
     gpr_slice_buffer_reset_and_unref(ep->read_buffer);
-    call_read_cb(exec_ctx, ep, 0);
+    call_read_cb(exec_ctx, ep, grpc_set_tsi_error_bits(
+                                   GRPC_ERROR_CREATE("Unwrap failed"), result));
     return;
   }
 
-  call_read_cb(exec_ctx, ep, 1);
+  call_read_cb(exec_ctx, ep, GRPC_ERROR_NONE);
 }
 
 static void endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep,
@@ -226,7 +230,7 @@
   if (ep->leftover_bytes.count) {
     gpr_slice_buffer_swap(&ep->leftover_bytes, &ep->source_buffer);
     GPR_ASSERT(ep->leftover_bytes.count == 0);
-    on_read(exec_ctx, ep, 1);
+    on_read(exec_ctx, ep, GRPC_ERROR_NONE);
     return;
   }
 
@@ -315,7 +319,10 @@
   if (result != TSI_OK) {
     /* TODO(yangg) do different things according to the error type? */
     gpr_slice_buffer_reset_and_unref(&ep->output_buffer);
-    grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL);
+    grpc_exec_ctx_push(
+        exec_ctx, cb,
+        grpc_set_tsi_error_bits(GRPC_ERROR_CREATE("Wrap failed"), result),
+        NULL);
     return;
   }
 
diff --git a/src/core/lib/security/secure_endpoint.h b/src/core/lib/security/transport/secure_endpoint.h
similarity index 91%
rename from src/core/lib/security/secure_endpoint.h
rename to src/core/lib/security/transport/secure_endpoint.h
index ff1c663..d00075b 100644
--- a/src/core/lib/security/secure_endpoint.h
+++ b/src/core/lib/security/transport/secure_endpoint.h
@@ -31,8 +31,8 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_SECURE_ENDPOINT_H
-#define GRPC_CORE_LIB_SECURITY_SECURE_ENDPOINT_H
+#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H
 
 #include <grpc/support/slice.h>
 #include "src/core/lib/iomgr/endpoint.h"
@@ -46,4 +46,4 @@
     struct tsi_frame_protector *protector, grpc_endpoint *to_wrap,
     gpr_slice *leftover_slices, size_t leftover_nslices);
 
-#endif /* GRPC_CORE_LIB_SECURITY_SECURE_ENDPOINT_H */
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H */
diff --git a/src/core/lib/security/security_connector.c b/src/core/lib/security/transport/security_connector.c
similarity index 98%
rename from src/core/lib/security/security_connector.c
rename to src/core/lib/security/transport/security_connector.c
index 2d2023b..1803dbe 100644
--- a/src/core/lib/security/security_connector.c
+++ b/src/core/lib/security/transport/security_connector.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/lib/security/security_connector.h"
+#include "src/core/lib/security/transport/security_connector.h"
 
 #include <stdbool.h>
 #include <string.h>
@@ -43,10 +43,10 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/ext/transport/chttp2/alpn/alpn.h"
-#include "src/core/lib/security/credentials.h"
-#include "src/core/lib/security/handshake.h"
-#include "src/core/lib/security/secure_endpoint.h"
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/transport/handshake.h"
+#include "src/core/lib/security/transport/secure_endpoint.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/load_file.h"
 #include "src/core/lib/support/string.h"
@@ -635,7 +635,8 @@
   char *default_root_certs_path =
       gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR);
   if (default_root_certs_path != NULL) {
-    result = gpr_load_file(default_root_certs_path, 0, NULL);
+    GRPC_LOG_IF_ERROR("load_file",
+                      gpr_load_file(default_root_certs_path, 0, &result));
     gpr_free(default_root_certs_path);
   }
 
@@ -653,7 +654,8 @@
   /* Fall back to installed certs if needed. */
   if (GPR_SLICE_IS_EMPTY(result) &&
       ovrd_res != GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY) {
-    result = gpr_load_file(installed_roots_path, 0, NULL);
+    GRPC_LOG_IF_ERROR("load_file",
+                      gpr_load_file(installed_roots_path, 0, &result));
   }
   return result;
 }
diff --git a/src/core/lib/security/security_connector.h b/src/core/lib/security/transport/security_connector.h
similarity index 97%
rename from src/core/lib/security/security_connector.h
rename to src/core/lib/security/transport/security_connector.h
index 2c893cd..84e586d 100644
--- a/src/core/lib/security/security_connector.h
+++ b/src/core/lib/security/transport/security_connector.h
@@ -31,8 +31,8 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_H
-#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_H
+#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_CONNECTOR_H
+#define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_CONNECTOR_H
 
 #include <grpc/grpc_security.h>
 #include "src/core/lib/iomgr/endpoint.h"
@@ -263,4 +263,4 @@
     const grpc_auth_context *auth_context);
 void tsi_shallow_peer_destruct(tsi_peer *peer);
 
-#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_H */
+#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_CONNECTOR_H */
diff --git a/src/core/lib/security/server_auth_filter.c b/src/core/lib/security/transport/server_auth_filter.c
similarity index 93%
rename from src/core/lib/security/server_auth_filter.c
rename to src/core/lib/security/transport/server_auth_filter.c
index 3320497..d45ec40 100644
--- a/src/core/lib/security/server_auth_filter.c
+++ b/src/core/lib/security/transport/server_auth_filter.c
@@ -33,9 +33,9 @@
 
 #include <string.h>
 
-#include "src/core/lib/security/auth_filters.h"
-#include "src/core/lib/security/credentials.h"
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/transport/auth_filters.h"
 
 #include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
@@ -128,7 +128,7 @@
     grpc_metadata_batch_filter(calld->recv_initial_metadata, remove_consumed_md,
                                elem);
     grpc_metadata_array_destroy(&calld->md);
-    calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 1);
+    grpc_exec_ctx_push(&exec_ctx, calld->on_done_recv, GRPC_ERROR_NONE, NULL);
   } else {
     gpr_slice message;
     grpc_transport_stream_op close_op;
@@ -146,18 +146,21 @@
     calld->transport_op.send_trailing_metadata = NULL;
     grpc_transport_stream_op_add_close(&close_op, status, &message);
     grpc_call_next_op(&exec_ctx, elem, &close_op);
-    calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 0);
+    grpc_exec_ctx_push(&exec_ctx, calld->on_done_recv,
+                       grpc_error_set_int(GRPC_ERROR_CREATE(error_details),
+                                          GRPC_ERROR_INT_GRPC_STATUS, status),
+                       NULL);
   }
 
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void auth_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
-                         bool success) {
+                         grpc_error *error) {
   grpc_call_element *elem = user_data;
   call_data *calld = elem->call_data;
   channel_data *chand = elem->channel_data;
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     if (chand->creds->processor.process != NULL) {
       calld->md = metadata_batch_to_md_array(calld->recv_initial_metadata);
       chand->creds->processor.process(
@@ -166,7 +169,8 @@
       return;
     }
   }
-  calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
+  grpc_exec_ctx_push(exec_ctx, calld->on_done_recv, GRPC_ERROR_REF(error),
+                     NULL);
 }
 
 static void set_recv_ops_md_callbacks(grpc_call_element *elem,
diff --git a/src/core/lib/security/auth_filters.h b/src/core/lib/security/transport/tsi_error.c
similarity index 81%
copy from src/core/lib/security/auth_filters.h
copy to src/core/lib/security/transport/tsi_error.c
index 7fb56c3..b9fb814 100644
--- a/src/core/lib/security/auth_filters.h
+++ b/src/core/lib/security/transport/tsi_error.c
@@ -31,12 +31,10 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
-#define GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
+#include "src/core/lib/security/transport/tsi_error.h"
 
-#include "src/core/lib/channel/channel_stack.h"
-
-extern const grpc_channel_filter grpc_client_auth_filter;
-extern const grpc_channel_filter grpc_server_auth_filter;
-
-#endif /* GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H */
+grpc_error *grpc_set_tsi_error_bits(grpc_error *error, tsi_result result) {
+  return grpc_error_set_int(grpc_error_set_str(error, GRPC_ERROR_STR_TSI_ERROR,
+                                               tsi_result_to_string(result)),
+                            GRPC_ERROR_INT_TSI_CODE, result);
+}
diff --git a/src/core/lib/security/auth_filters.h b/src/core/lib/security/transport/tsi_error.h
similarity index 83%
copy from src/core/lib/security/auth_filters.h
copy to src/core/lib/security/transport/tsi_error.h
index 7fb56c3..5406b84 100644
--- a/src/core/lib/security/auth_filters.h
+++ b/src/core/lib/security/transport/tsi_error.h
@@ -31,12 +31,12 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
-#define GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
+#ifndef TSI_ERROR_H
+#define TSI_ERROR_H
 
-#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/tsi/transport_security_interface.h"
 
-extern const grpc_channel_filter grpc_client_auth_filter;
-extern const grpc_channel_filter grpc_server_auth_filter;
+grpc_error *grpc_set_tsi_error_bits(grpc_error *error, tsi_result result);
 
-#endif /* GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H */
+#endif
diff --git a/src/core/lib/security/b64.c b/src/core/lib/security/util/b64.c
similarity index 99%
rename from src/core/lib/security/b64.c
rename to src/core/lib/security/util/b64.c
index 87f0e05..9da42e4 100644
--- a/src/core/lib/security/b64.c
+++ b/src/core/lib/security/util/b64.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/lib/security/b64.h"
+#include "src/core/lib/security/util/b64.h"
 
 #include <stdint.h>
 #include <string.h>
diff --git a/src/core/lib/security/b64.h b/src/core/lib/security/util/b64.h
similarity index 94%
rename from src/core/lib/security/b64.h
rename to src/core/lib/security/util/b64.h
index c515e7a..6908095 100644
--- a/src/core/lib/security/b64.h
+++ b/src/core/lib/security/util/b64.h
@@ -31,8 +31,8 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_B64_H
-#define GRPC_CORE_LIB_SECURITY_B64_H
+#ifndef GRPC_CORE_LIB_SECURITY_UTIL_B64_H
+#define GRPC_CORE_LIB_SECURITY_UTIL_B64_H
 
 #include <grpc/support/slice.h>
 
@@ -49,4 +49,4 @@
 gpr_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len,
                                       int url_safe);
 
-#endif /* GRPC_CORE_LIB_SECURITY_B64_H */
+#endif /* GRPC_CORE_LIB_SECURITY_UTIL_B64_H */
diff --git a/src/core/lib/security/credentials_win32.c b/src/core/lib/security/util/json_util.c
similarity index 65%
copy from src/core/lib/security/credentials_win32.c
copy to src/core/lib/security/util/json_util.c
index d29847a..7eed039 100644
--- a/src/core/lib/security/credentials_win32.c
+++ b/src/core/lib/security/util/json_util.c
@@ -31,31 +31,31 @@
  *
  */
 
-#include <grpc/support/port_platform.h>
+#include "src/core/lib/security/util/json_util.h"
 
-#ifdef GPR_WIN32
+#include <string.h>
 
-#include "src/core/lib/security/credentials.h"
-
-#include <grpc/support/alloc.h>
 #include <grpc/support/log.h>
 #include <grpc/support/string_util.h>
 
-#include "src/core/lib/support/env.h"
-#include "src/core/lib/support/string.h"
-
-char *grpc_get_well_known_google_credentials_file_path_impl(void) {
-  char *result = NULL;
-  char *appdata_path = gpr_getenv("APPDATA");
-  if (appdata_path == NULL) {
-    gpr_log(GPR_ERROR, "Could not get APPDATA environment variable.");
+const char *grpc_json_get_string_property(const grpc_json *json,
+                                          const char *prop_name) {
+  grpc_json *child;
+  for (child = json->child; child != NULL; child = child->next) {
+    if (strcmp(child->key, prop_name) == 0) break;
+  }
+  if (child == NULL || child->type != GRPC_JSON_STRING) {
+    gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name);
     return NULL;
   }
-  gpr_asprintf(&result, "%s/%s/%s", appdata_path,
-               GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY,
-               GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE);
-  gpr_free(appdata_path);
-  return result;
+  return child->value;
 }
 
-#endif /* GPR_WIN32 */
+bool grpc_copy_json_string_property(const grpc_json *json,
+                                    const char *prop_name,
+                                    char **copied_value) {
+  const char *prop_value = grpc_json_get_string_property(json, prop_name);
+  if (prop_value == NULL) return false;
+  *copied_value = gpr_strdup(prop_value);
+  return true;
+}
diff --git a/src/core/lib/security/secure_endpoint.h b/src/core/lib/security/util/json_util.h
similarity index 65%
copy from src/core/lib/security/secure_endpoint.h
copy to src/core/lib/security/util/json_util.h
index ff1c663..5959626 100644
--- a/src/core/lib/security/secure_endpoint.h
+++ b/src/core/lib/security/util/json_util.h
@@ -31,19 +31,25 @@
  *
  */
 
-#ifndef GRPC_CORE_LIB_SECURITY_SECURE_ENDPOINT_H
-#define GRPC_CORE_LIB_SECURITY_SECURE_ENDPOINT_H
+#ifndef GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H
+#define GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H
 
-#include <grpc/support/slice.h>
-#include "src/core/lib/iomgr/endpoint.h"
+#include <stdbool.h>
 
-struct tsi_frame_protector;
+#include "src/core/lib/json/json.h"
 
-extern int grpc_trace_secure_endpoint;
+// Constants.
+#define GRPC_AUTH_JSON_TYPE_INVALID "invalid"
+#define GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT "service_account"
+#define GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER "authorized_user"
 
-/* Takes ownership of protector and to_wrap, and refs leftover_slices. */
-grpc_endpoint *grpc_secure_endpoint_create(
-    struct tsi_frame_protector *protector, grpc_endpoint *to_wrap,
-    gpr_slice *leftover_slices, size_t leftover_nslices);
+// Gets a child property from a json node.
+const char *grpc_json_get_string_property(const grpc_json *json,
+                                          const char *prop_name);
 
-#endif /* GRPC_CORE_LIB_SECURITY_SECURE_ENDPOINT_H */
+// Copies the value of the json child property specified by prop_name.
+// Returns false if the property was not found.
+bool grpc_copy_json_string_property(const grpc_json *json,
+                                    const char *prop_name, char **copied_value);
+
+#endif  // GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H
diff --git a/src/core/lib/support/avl.c b/src/core/lib/support/avl.c
index 8d3ce23..acf8fd5 100644
--- a/src/core/lib/support/avl.c
+++ b/src/core/lib/support/avl.c
@@ -124,6 +124,15 @@
   return node ? node->value : NULL;
 }
 
+int gpr_avl_maybe_get(gpr_avl avl, void *key, void **value) {
+  gpr_avl_node *node = get(avl.vtable, avl.root, key);
+  if (node != NULL) {
+    *value = node->value;
+    return 1;
+  }
+  return 0;
+}
+
 static gpr_avl_node *rotate_left(const gpr_avl_vtable *vtable, void *key,
                                  void *value, gpr_avl_node *left,
                                  gpr_avl_node *right) {
@@ -286,3 +295,5 @@
 }
 
 void gpr_avl_unref(gpr_avl avl) { unref_node(avl.vtable, avl.root); }
+
+int gpr_avl_is_empty(gpr_avl avl) { return avl.root == NULL; }
diff --git a/src/core/lib/support/load_file.c b/src/core/lib/support/load_file.c
index f30aacd..a78bb33 100644
--- a/src/core/lib/support/load_file.c
+++ b/src/core/lib/support/load_file.c
@@ -43,21 +43,19 @@
 #include "src/core/lib/support/block_annotate.h"
 #include "src/core/lib/support/string.h"
 
-gpr_slice gpr_load_file(const char *filename, int add_null_terminator,
-                        int *success) {
+grpc_error *gpr_load_file(const char *filename, int add_null_terminator,
+                          gpr_slice *output) {
   unsigned char *contents = NULL;
   size_t contents_size = 0;
-  char *error_msg = NULL;
   gpr_slice result = gpr_empty_slice();
   FILE *file;
   size_t bytes_read = 0;
+  grpc_error *error = GRPC_ERROR_NONE;
 
   GRPC_SCHEDULING_START_BLOCKING_REGION;
   file = fopen(filename, "rb");
   if (file == NULL) {
-    gpr_asprintf(&error_msg, "Could not open file %s (error = %s).", filename,
-                 strerror(errno));
-    GPR_ASSERT(error_msg != NULL);
+    error = GRPC_OS_ERROR(errno, "fopen");
     goto end;
   }
   fseek(file, 0, SEEK_END);
@@ -67,25 +65,25 @@
   contents = gpr_malloc(contents_size + (add_null_terminator ? 1 : 0));
   bytes_read = fread(contents, 1, contents_size, file);
   if (bytes_read < contents_size) {
+    error = GRPC_OS_ERROR(errno, "fread");
     GPR_ASSERT(ferror(file));
-    gpr_asprintf(&error_msg, "Error %s occured while reading file %s.",
-                 strerror(errno), filename);
-    GPR_ASSERT(error_msg != NULL);
     goto end;
   }
-  if (success != NULL) *success = 1;
   if (add_null_terminator) {
     contents[contents_size++] = 0;
   }
   result = gpr_slice_new(contents, contents_size, gpr_free);
 
 end:
-  if (error_msg != NULL) {
-    gpr_log(GPR_ERROR, "%s", error_msg);
-    gpr_free(error_msg);
-    if (success != NULL) *success = 0;
-  }
+  *output = result;
   if (file != NULL) fclose(file);
+  if (error != GRPC_ERROR_NONE) {
+    grpc_error *error_out = grpc_error_set_str(
+        GRPC_ERROR_CREATE_REFERENCING("Failed to load file", &error, 1),
+        GRPC_ERROR_STR_FILENAME, filename);
+    GRPC_ERROR_UNREF(error);
+    error = error_out;
+  }
   GRPC_SCHEDULING_END_BLOCKING_REGION;
-  return result;
+  return error;
 }
diff --git a/src/core/lib/support/load_file.h b/src/core/lib/support/load_file.h
index 9a4b279..d4008a4 100644
--- a/src/core/lib/support/load_file.h
+++ b/src/core/lib/support/load_file.h
@@ -38,15 +38,16 @@
 
 #include <grpc/support/slice.h>
 
+#include "src/core/lib/iomgr/error.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /* Loads the content of a file into a slice. add_null_terminator will add
-   a NULL terminator if non-zero. The success parameter, if not NULL,
-   will be set to 1 in case of success and 0 in case of failure. */
-gpr_slice gpr_load_file(const char *filename, int add_null_terminator,
-                        int *success);
+   a NULL terminator if non-zero. */
+grpc_error *gpr_load_file(const char *filename, int add_null_terminator,
+                          gpr_slice *slice);
 
 #ifdef __cplusplus
 }
diff --git a/src/core/lib/surface/alarm.c b/src/core/lib/surface/alarm.c
index 2cf2f00..aa9d60e 100644
--- a/src/core/lib/surface/alarm.c
+++ b/src/core/lib/surface/alarm.c
@@ -48,9 +48,9 @@
 static void do_nothing_end_completion(grpc_exec_ctx *exec_ctx, void *arg,
                                       grpc_cq_completion *c) {}
 
-static void alarm_cb(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void alarm_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   grpc_alarm *alarm = arg;
-  grpc_cq_end_op(exec_ctx, alarm->cq, alarm->tag, success,
+  grpc_cq_end_op(exec_ctx, alarm->cq, alarm->tag, error,
                  do_nothing_end_completion, NULL, &alarm->completion);
 }
 
diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
index 9b2b94e..7709ef5 100644
--- a/src/core/lib/surface/call.c
+++ b/src/core/lib/surface/call.c
@@ -122,6 +122,7 @@
   grpc_closure finish_batch;
   void *notify_tag;
   gpr_refcount steps_to_complete;
+  grpc_error *error;
 
   uint8_t send_initial_metadata;
   uint8_t send_message;
@@ -130,7 +131,6 @@
   uint8_t recv_message;
   uint8_t recv_final_op;
   uint8_t is_notify_tag_closure;
-  uint8_t success;
 } batch_control;
 
 struct grpc_call {
@@ -239,9 +239,9 @@
                                           grpc_status_code status,
                                           const char *description);
 static void destroy_call(grpc_exec_ctx *exec_ctx, void *call_stack,
-                         bool success);
+                         grpc_error *error);
 static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp,
-                                  bool success);
+                                  grpc_error *error);
 
 grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call,
                             uint32_t propagation_mask,
@@ -361,7 +361,8 @@
   GRPC_CALL_STACK_UNREF(exec_ctx, CALL_STACK_FROM_CALL(c), REF_REASON);
 }
 
-static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, bool success) {
+static void destroy_call(grpc_exec_ctx *exec_ctx, void *call,
+                         grpc_error *error) {
   size_t i;
   int ii;
   grpc_call *c = call;
@@ -706,13 +707,13 @@
   grpc_status_code status;
 } cancel_closure;
 
-static void done_cancel(grpc_exec_ctx *exec_ctx, void *ccp, bool success) {
+static void done_cancel(grpc_exec_ctx *exec_ctx, void *ccp, grpc_error *error) {
   cancel_closure *cc = ccp;
   GRPC_CALL_INTERNAL_UNREF(exec_ctx, cc->call, "cancel");
   gpr_free(cc);
 }
 
-static void send_cancel(grpc_exec_ctx *exec_ctx, void *ccp, bool success) {
+static void send_cancel(grpc_exec_ctx *exec_ctx, void *ccp, grpc_error *error) {
   grpc_transport_stream_op op;
   cancel_closure *cc = ccp;
   memset(&op, 0, sizeof(op));
@@ -739,7 +740,7 @@
   cc->call = c;
   cc->status = status;
   GRPC_CALL_INTERNAL_REF(c, "cancel");
-  grpc_exec_ctx_enqueue(exec_ctx, &cc->closure, true, NULL);
+  grpc_exec_ctx_push(exec_ctx, &cc->closure, GRPC_ERROR_NONE, NULL);
 
   return GRPC_CALL_OK;
 }
@@ -775,11 +776,11 @@
   return CALL_FROM_TOP_ELEM(elem);
 }
 
-static void call_alarm(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void call_alarm(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   grpc_call *call = arg;
   gpr_mu_lock(&call->mu);
   call->have_alarm = 0;
-  if (success) {
+  if (error != GRPC_ERROR_CANCELLED) {
     cancel_with_status(exec_ctx, call, GRPC_STATUS_DEADLINE_EXCEEDED,
                        "Deadline Exceeded");
   }
@@ -961,7 +962,8 @@
                                   batch_control *bctl) {
   grpc_call *call = bctl->call;
   if (bctl->is_notify_tag_closure) {
-    grpc_exec_ctx_enqueue(exec_ctx, bctl->notify_tag, bctl->success, NULL);
+    /* unrefs bctl->error */
+    grpc_exec_ctx_push(exec_ctx, bctl->notify_tag, bctl->error, NULL);
     gpr_mu_lock(&call->mu);
     bctl->call->used_batches =
         (uint8_t)(bctl->call->used_batches &
@@ -969,7 +971,8 @@
     gpr_mu_unlock(&call->mu);
     GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion");
   } else {
-    grpc_cq_end_op(exec_ctx, bctl->call->cq, bctl->notify_tag, bctl->success,
+    /* unrefs bctl->error */
+    grpc_cq_end_op(exec_ctx, bctl->call->cq, bctl->notify_tag, bctl->error,
                    finish_batch_completion, bctl, &bctl->cq_completion);
   }
 }
@@ -1001,11 +1004,11 @@
 }
 
 static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp,
-                                  bool success) {
+                                  grpc_error *error) {
   batch_control *bctl = bctlp;
   grpc_call *call = bctl->call;
 
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     gpr_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer,
                          call->receiving_slice);
     continue_receiving_slices(exec_ctx, bctl);
@@ -1058,15 +1061,15 @@
 }
 
 static void receiving_stream_ready(grpc_exec_ctx *exec_ctx, void *bctlp,
-                                   bool success) {
+                                   grpc_error *error) {
   batch_control *bctl = bctlp;
   grpc_call *call = bctl->call;
 
   gpr_mu_lock(&bctl->call->mu);
-  if (bctl->call->has_initial_md_been_received || !success ||
+  if (bctl->call->has_initial_md_been_received || error != GRPC_ERROR_NONE ||
       call->receiving_stream == NULL) {
     gpr_mu_unlock(&bctl->call->mu);
-    process_data_after_md(exec_ctx, bctlp, success);
+    process_data_after_md(exec_ctx, bctlp, error);
   } else {
     call->saved_receiving_stream_ready_bctlp = bctlp;
     gpr_mu_unlock(&bctl->call->mu);
@@ -1074,14 +1077,14 @@
 }
 
 static void receiving_initial_metadata_ready(grpc_exec_ctx *exec_ctx,
-                                             void *bctlp, bool success) {
+                                             void *bctlp, grpc_error *error) {
   batch_control *bctl = bctlp;
   grpc_call *call = bctl->call;
 
   gpr_mu_lock(&call->mu);
 
-  if (!success) {
-    bctl->success = false;
+  if (error != GRPC_ERROR_NONE) {
+    bctl->error = GRPC_ERROR_REF(error);
   } else {
     grpc_metadata_batch *md =
         &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
@@ -1101,7 +1104,7 @@
     grpc_closure *saved_rsr_closure = grpc_closure_create(
         receiving_stream_ready, call->saved_receiving_stream_ready_bctlp);
     call->saved_receiving_stream_ready_bctlp = NULL;
-    grpc_exec_ctx_enqueue(exec_ctx, saved_rsr_closure, success, NULL);
+    grpc_exec_ctx_push(exec_ctx, saved_rsr_closure, error, NULL);
   }
 
   gpr_mu_unlock(&call->mu);
@@ -1111,15 +1114,18 @@
   }
 }
 
-static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp, bool success) {
+static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp,
+                         grpc_error *error) {
   batch_control *bctl = bctlp;
   grpc_call *call = bctl->call;
   grpc_call *child_call;
   grpc_call *next_child_call;
 
+  GRPC_ERROR_REF(error);
+
   gpr_mu_lock(&call->mu);
   if (bctl->send_initial_metadata) {
-    if (!success) {
+    if (error != GRPC_ERROR_NONE) {
       set_status_code(call, STATUS_FROM_CORE, GRPC_STATUS_UNAVAILABLE);
     }
     grpc_metadata_batch_destroy(
@@ -1165,13 +1171,17 @@
                        call->final_op.server.cancelled);
     }
 
-    success = 1;
+    GRPC_ERROR_UNREF(error);
+    error = GRPC_ERROR_NONE;
   }
-  bctl->success = success != 0;
+  GRPC_ERROR_UNREF(bctl->error);
+  bctl->error = GRPC_ERROR_REF(error);
   gpr_mu_unlock(&call->mu);
   if (gpr_unref(&bctl->steps_to_complete)) {
     post_batch_completion(exec_ctx, bctl);
   }
+
+  GRPC_ERROR_UNREF(error);
 }
 
 static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx,
@@ -1201,7 +1211,7 @@
 
   if (nops == 0) {
     GRPC_CALL_INTERNAL_REF(call, "completion");
-    bctl->success = 1;
+    bctl->error = GRPC_ERROR_NONE;
     if (!is_notify_tag_closure) {
       grpc_cq_begin_op(call->cq, notify_tag);
     }
diff --git a/src/core/lib/surface/channel.c b/src/core/lib/surface/channel.c
index b6b760b..3c68dc0 100644
--- a/src/core/lib/surface/channel.c
+++ b/src/core/lib/surface/channel.c
@@ -80,7 +80,8 @@
 /* the protobuf library will (by default) start warning at 100megs */
 #define DEFAULT_MAX_MESSAGE_LENGTH (100 * 1024 * 1024)
 
-static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg, bool success);
+static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg,
+                            grpc_error *error);
 
 grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target,
                                   const grpc_channel_args *input_args,
@@ -267,7 +268,7 @@
 }
 
 static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg,
-                            bool iomgr_success) {
+                            grpc_error *error) {
   grpc_channel *channel = arg;
   grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(channel));
   while (channel->registered_calls) {
@@ -293,7 +294,7 @@
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   GRPC_API_TRACE("grpc_channel_destroy(channel=%p)", 1, (channel));
   memset(&op, 0, sizeof(op));
-  op.disconnect = 1;
+  op.disconnect_with_error = GRPC_ERROR_CREATE("Channel Destroyed");
   elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0);
   elem->filter->start_transport_op(&exec_ctx, elem, &op);
 
diff --git a/src/core/lib/surface/channel_ping.c b/src/core/lib/surface/channel_ping.c
index 5a50698..9818f9d 100644
--- a/src/core/lib/surface/channel_ping.c
+++ b/src/core/lib/surface/channel_ping.c
@@ -53,10 +53,10 @@
   gpr_free(arg);
 }
 
-static void ping_done(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void ping_done(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   ping_result *pr = arg;
-  grpc_cq_end_op(exec_ctx, pr->cq, pr->tag, success, ping_destroy, pr,
-                 &pr->completion_storage);
+  grpc_cq_end_op(exec_ctx, pr->cq, pr->tag, GRPC_ERROR_REF(error), ping_destroy,
+                 pr, &pr->completion_storage);
 }
 
 void grpc_channel_ping(grpc_channel *channel, grpc_completion_queue *cq,
diff --git a/src/core/lib/surface/completion_queue.c b/src/core/lib/surface/completion_queue.c
index 1f82c3b..c1a80a7 100644
--- a/src/core/lib/surface/completion_queue.c
+++ b/src/core/lib/surface/completion_queue.c
@@ -89,7 +89,7 @@
 static grpc_completion_queue *g_freelist;
 
 static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *cc,
-                                     bool success);
+                                     grpc_error *error);
 
 void grpc_cq_global_init(void) { gpr_mu_init(&g_freelist_mu); }
 
@@ -172,7 +172,7 @@
 }
 
 static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *arg,
-                                     bool success) {
+                                     grpc_error *error) {
   grpc_completion_queue *cc = arg;
   GRPC_CQ_INTERNAL_UNREF(cc, "pollset_destroy");
 }
@@ -215,7 +215,7 @@
    event, then enter shutdown mode */
 /* Queue a GRPC_OP_COMPLETED operation */
 void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc,
-                    void *tag, int success,
+                    void *tag, grpc_error *error,
                     void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg,
                                  grpc_cq_completion *storage),
                     void *done_arg, grpc_cq_completion *storage) {
@@ -227,16 +227,20 @@
 #endif
 
   GPR_TIMER_BEGIN("grpc_cq_end_op", 0);
-  GRPC_API_TRACE(
-      "grpc_cq_end_op(exec_ctx=%p, cc=%p, tag=%p, success=%d, done=%p, "
-      "done_arg=%p, storage=%p)",
-      7, (exec_ctx, cc, tag, success, done, done_arg, storage));
+  if (grpc_api_trace) {
+    const char *errmsg = grpc_error_string(error);
+    GRPC_API_TRACE(
+        "grpc_cq_end_op(exec_ctx=%p, cc=%p, tag=%p, error=%s, done=%p, "
+        "done_arg=%p, storage=%p)",
+        7, (exec_ctx, cc, tag, errmsg, done, done_arg, storage));
+    grpc_error_free_string(errmsg);
+  }
 
   storage->tag = tag;
   storage->done = done;
   storage->done_arg = done_arg;
-  storage->next =
-      ((uintptr_t)&cc->completed_head) | ((uintptr_t)(success != 0));
+  storage->next = ((uintptr_t)&cc->completed_head) |
+                  ((uintptr_t)(error == GRPC_ERROR_NONE));
 
   gpr_mu_lock(cc->mu);
 #ifndef NDEBUG
@@ -263,8 +267,15 @@
         break;
       }
     }
-    grpc_pollset_kick(POLLSET_FROM_CQ(cc), pluck_worker);
+    grpc_error *kick_error =
+        grpc_pollset_kick(POLLSET_FROM_CQ(cc), pluck_worker);
     gpr_mu_unlock(cc->mu);
+    if (kick_error != GRPC_ERROR_NONE) {
+      const char *msg = grpc_error_string(kick_error);
+      gpr_log(GPR_ERROR, "Kick failed: %s", msg);
+      grpc_error_free_string(msg);
+      GRPC_ERROR_UNREF(kick_error);
+    }
   } else {
     cc->completed_tail->next =
         ((uintptr_t)storage) | (1u & (uintptr_t)cc->completed_tail->next);
@@ -278,6 +289,8 @@
   }
 
   GPR_TIMER_END("grpc_cq_end_op", 0);
+
+  GRPC_ERROR_UNREF(error);
 }
 
 grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
@@ -343,8 +356,18 @@
       gpr_mu_lock(cc->mu);
       continue;
     } else {
-      grpc_pollset_work(&exec_ctx, POLLSET_FROM_CQ(cc), &worker, now,
-                        iteration_deadline);
+      grpc_error *err = grpc_pollset_work(&exec_ctx, POLLSET_FROM_CQ(cc),
+                                          &worker, now, iteration_deadline);
+      if (err != GRPC_ERROR_NONE) {
+        gpr_mu_unlock(cc->mu);
+        const char *msg = grpc_error_string(err);
+        gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg);
+        grpc_error_free_string(msg);
+        GRPC_ERROR_UNREF(err);
+        memset(&ret, 0, sizeof(ret));
+        ret.type = GRPC_QUEUE_TIMEOUT;
+        break;
+      }
     }
   }
   GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
@@ -460,8 +483,19 @@
       grpc_exec_ctx_flush(&exec_ctx);
       gpr_mu_lock(cc->mu);
     } else {
-      grpc_pollset_work(&exec_ctx, POLLSET_FROM_CQ(cc), &worker, now,
-                        iteration_deadline);
+      grpc_error *err = grpc_pollset_work(&exec_ctx, POLLSET_FROM_CQ(cc),
+                                          &worker, now, iteration_deadline);
+      if (err != GRPC_ERROR_NONE) {
+        del_plucker(cc, tag, &worker);
+        gpr_mu_unlock(cc->mu);
+        const char *msg = grpc_error_string(err);
+        gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg);
+        grpc_error_free_string(msg);
+        GRPC_ERROR_UNREF(err);
+        memset(&ret, 0, sizeof(ret));
+        ret.type = GRPC_QUEUE_TIMEOUT;
+        break;
+      }
     }
     del_plucker(cc, tag, &worker);
   }
diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h
index eef82cf..9da660c 100644
--- a/src/core/lib/surface/completion_queue.h
+++ b/src/core/lib/surface/completion_queue.h
@@ -75,7 +75,7 @@
 /* Queue a GRPC_OP_COMPLETED operation; tag must correspond to the tag passed to
    grpc_cq_begin_op */
 void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc,
-                    void *tag, int success,
+                    void *tag, grpc_error *error,
                     void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg,
                                  grpc_cq_completion *storage),
                     void *done_arg, grpc_cq_completion *storage);
diff --git a/src/core/lib/surface/init_secure.c b/src/core/lib/surface/init_secure.c
index 3fda2c9..7ee7b51 100644
--- a/src/core/lib/surface/init_secure.c
+++ b/src/core/lib/surface/init_secure.c
@@ -37,10 +37,10 @@
 #include <string.h>
 
 #include "src/core/lib/debug/trace.h"
-#include "src/core/lib/security/auth_filters.h"
-#include "src/core/lib/security/credentials.h"
-#include "src/core/lib/security/secure_endpoint.h"
-#include "src/core/lib/security/security_connector.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/transport/auth_filters.h"
+#include "src/core/lib/security/transport/secure_endpoint.h"
+#include "src/core/lib/security/transport/security_connector.h"
 #include "src/core/lib/surface/channel_init.h"
 #include "src/core/lib/tsi/transport_security_interface.h"
 
diff --git a/src/core/lib/surface/lame_client.c b/src/core/lib/surface/lame_client.c
index f50ec54..5c15b67 100644
--- a/src/core/lib/surface/lame_client.c
+++ b/src/core/lib/surface/lame_client.c
@@ -80,7 +80,8 @@
   } else if (op->recv_trailing_metadata != NULL) {
     fill_metadata(elem, op->recv_trailing_metadata);
   }
-  grpc_transport_stream_op_finish_with_failure(exec_ctx, op);
+  grpc_transport_stream_op_finish_with_failure(
+      exec_ctx, op, GRPC_ERROR_CREATE("lame client channel"));
 }
 
 static char *lame_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
@@ -93,15 +94,17 @@
   if (op->on_connectivity_state_change) {
     GPR_ASSERT(*op->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE);
     *op->connectivity_state = GRPC_CHANNEL_FATAL_FAILURE;
-    op->on_connectivity_state_change->cb(
-        exec_ctx, op->on_connectivity_state_change->cb_arg, 1);
+    grpc_exec_ctx_push(exec_ctx, op->on_connectivity_state_change,
+                       GRPC_ERROR_NONE, NULL);
   }
   if (op->on_consumed != NULL) {
-    op->on_consumed->cb(exec_ctx, op->on_consumed->cb_arg, 1);
+    grpc_exec_ctx_push(exec_ctx, op->on_consumed, GRPC_ERROR_NONE, NULL);
   }
   if (op->send_ping != NULL) {
-    op->send_ping->cb(exec_ctx, op->send_ping->cb_arg, 0);
+    grpc_exec_ctx_push(exec_ctx, op->send_ping,
+                       GRPC_ERROR_CREATE("lame client channel"), NULL);
   }
+  GRPC_ERROR_UNREF(op->disconnect_with_error);
 }
 
 static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
index 2db95b9..9710f9e 100644
--- a/src/core/lib/surface/server.c
+++ b/src/core/lib/surface/server.c
@@ -232,9 +232,10 @@
 #define SERVER_FROM_CALL_ELEM(elem) \
   (((channel_data *)(elem)->channel_data)->server)
 
-static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *calld, bool success);
+static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *calld,
+                            grpc_error *error);
 static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server,
-                      requested_call *rc);
+                      requested_call *rc, grpc_error *error);
 /* Before calling maybe_finish_shutdown, we must hold mu_global and not
    hold mu_call */
 static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_server *server);
@@ -265,14 +266,14 @@
 };
 
 static void shutdown_cleanup(grpc_exec_ctx *exec_ctx, void *arg,
-                             bool iomgr_status_ignored) {
+                             grpc_error *error) {
   struct shutdown_cleanup_args *a = arg;
   gpr_slice_unref(a->slice);
   gpr_free(a);
 }
 
 static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel,
-                          int send_goaway, int send_disconnect) {
+                          int send_goaway, grpc_error *send_disconnect) {
   grpc_transport_op op;
   struct shutdown_cleanup_args *sc;
   grpc_channel_element *elem;
@@ -283,7 +284,7 @@
   sc->slice = gpr_slice_from_copied_string("Server shutdown");
   op.goaway_message = &sc->slice;
   op.goaway_status = GRPC_STATUS_OK;
-  op.disconnect = send_disconnect;
+  op.disconnect_with_error = send_disconnect;
   grpc_closure_init(&sc->closure, shutdown_cleanup, sc);
   op.on_consumed = &sc->closure;
 
@@ -294,14 +295,16 @@
 static void channel_broadcaster_shutdown(grpc_exec_ctx *exec_ctx,
                                          channel_broadcaster *cb,
                                          int send_goaway,
-                                         int force_disconnect) {
+                                         grpc_error *force_disconnect) {
   size_t i;
 
   for (i = 0; i < cb->num_channels; i++) {
-    send_shutdown(exec_ctx, cb->channels[i], send_goaway, force_disconnect);
+    send_shutdown(exec_ctx, cb->channels[i], send_goaway,
+                  GRPC_ERROR_REF(force_disconnect));
     GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, cb->channels[i], "broadcast");
   }
   gpr_free(cb->channels);
+  GRPC_ERROR_UNREF(force_disconnect);
 }
 
 /*
@@ -320,7 +323,8 @@
   gpr_stack_lockfree_destroy(rm->requests);
 }
 
-static void kill_zombie(grpc_exec_ctx *exec_ctx, void *elem, bool success) {
+static void kill_zombie(grpc_exec_ctx *exec_ctx, void *elem,
+                        grpc_error *error) {
   grpc_call_destroy(grpc_call_from_top_element(elem));
 }
 
@@ -335,17 +339,21 @@
     grpc_closure_init(
         &calld->kill_zombie_closure, kill_zombie,
         grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0));
-    grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE,
+                       NULL);
   }
 }
 
 static void request_matcher_kill_requests(grpc_exec_ctx *exec_ctx,
                                           grpc_server *server,
-                                          request_matcher *rm) {
+                                          request_matcher *rm,
+                                          grpc_error *error) {
   int request_id;
   while ((request_id = gpr_stack_lockfree_pop(rm->requests)) != -1) {
-    fail_call(exec_ctx, server, &server->requested_calls[request_id]);
+    fail_call(exec_ctx, server, &server->requested_calls[request_id],
+              GRPC_ERROR_REF(error));
   }
+  GRPC_ERROR_UNREF(error);
 }
 
 /*
@@ -398,7 +406,7 @@
 }
 
 static void finish_destroy_channel(grpc_exec_ctx *exec_ctx, void *cd,
-                                   bool success) {
+                                   grpc_error *error) {
   channel_data *chand = cd;
   grpc_server *server = chand->server;
   GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "server");
@@ -487,23 +495,24 @@
       grpc_call_stack_element(grpc_call_get_call_stack(call), 0);
   channel_data *chand = elem->channel_data;
   server_ref(chand->server);
-  grpc_cq_end_op(exec_ctx, calld->cq_new, rc->tag, true, done_request_event, rc,
-                 &rc->completion);
+  grpc_cq_end_op(exec_ctx, calld->cq_new, rc->tag, GRPC_ERROR_NONE,
+                 done_request_event, rc, &rc->completion);
 }
 
-static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *arg,
+                            grpc_error *error) {
   call_data *calld = arg;
   request_matcher *rm = calld->request_matcher;
   grpc_server *server = rm->server;
 
-  if (!success || gpr_atm_acq_load(&server->shutdown_flag)) {
+  if (error != GRPC_ERROR_NONE || gpr_atm_acq_load(&server->shutdown_flag)) {
     gpr_mu_lock(&calld->mu_state);
     calld->state = ZOMBIED;
     gpr_mu_unlock(&calld->mu_state);
     grpc_closure_init(
         &calld->kill_zombie_closure, kill_zombie,
         grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0));
-    grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, &calld->kill_zombie_closure, error, NULL);
     return;
   }
 
@@ -540,7 +549,8 @@
     calld->state = ZOMBIED;
     gpr_mu_unlock(&calld->mu_state);
     grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
-    grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE,
+                       NULL);
     return;
   }
 
@@ -548,7 +558,7 @@
 
   switch (payload_handling) {
     case GRPC_SRM_PAYLOAD_NONE:
-      publish_new_rpc(exec_ctx, calld, true);
+      publish_new_rpc(exec_ctx, calld, GRPC_ERROR_NONE);
       break;
     case GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER: {
       grpc_op op;
@@ -636,16 +646,19 @@
 }
 
 static void kill_pending_work_locked(grpc_exec_ctx *exec_ctx,
-                                     grpc_server *server) {
+                                     grpc_server *server, grpc_error *error) {
   registered_method *rm;
   request_matcher_kill_requests(exec_ctx, server,
-                                &server->unregistered_request_matcher);
+                                &server->unregistered_request_matcher,
+                                GRPC_ERROR_REF(error));
   request_matcher_zombify_all_pending_calls(
       exec_ctx, &server->unregistered_request_matcher);
   for (rm = server->registered_methods; rm; rm = rm->next) {
-    request_matcher_kill_requests(exec_ctx, server, &rm->request_matcher);
+    request_matcher_kill_requests(exec_ctx, server, &rm->request_matcher,
+                                  GRPC_ERROR_REF(error));
     request_matcher_zombify_all_pending_calls(exec_ctx, &rm->request_matcher);
   }
+  GRPC_ERROR_UNREF(error);
 }
 
 static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx,
@@ -655,7 +668,8 @@
     return;
   }
 
-  kill_pending_work_locked(exec_ctx, server);
+  kill_pending_work_locked(exec_ctx, server,
+                           GRPC_ERROR_CREATE("Server Shutdown"));
 
   if (server->root_channel_data.next != &server->root_channel_data ||
       server->listeners_destroyed < num_listeners(server)) {
@@ -676,7 +690,8 @@
   for (i = 0; i < server->num_shutdown_tags; i++) {
     server_ref(server);
     grpc_cq_end_op(exec_ctx, server->shutdown_tags[i].cq,
-                   server->shutdown_tags[i].tag, 1, done_shutdown_event, server,
+                   server->shutdown_tags[i].tag, GRPC_ERROR_NONE,
+                   done_shutdown_event, server,
                    &server->shutdown_tags[i].completion);
   }
 }
@@ -699,11 +714,12 @@
 }
 
 static void server_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr,
-                                            bool success) {
+                                            grpc_error *error) {
   grpc_call_element *elem = ptr;
   call_data *calld = elem->call_data;
   gpr_timespec op_deadline;
 
+  GRPC_ERROR_REF(error);
   grpc_metadata_batch_filter(calld->recv_initial_metadata, server_filter, elem);
   op_deadline = calld->recv_initial_metadata->deadline;
   if (0 != gpr_time_cmp(op_deadline, gpr_inf_future(op_deadline.clock_type))) {
@@ -712,11 +728,13 @@
   if (calld->host && calld->path) {
     /* do nothing */
   } else {
-    success = 0;
+    GRPC_ERROR_UNREF(error);
+    error =
+        GRPC_ERROR_CREATE_REFERENCING("Missing :authority or :path", &error, 1);
   }
 
-  calld->on_done_recv_initial_metadata->cb(
-      exec_ctx, calld->on_done_recv_initial_metadata->cb_arg, success);
+  grpc_exec_ctx_push(exec_ctx, calld->on_done_recv_initial_metadata, error,
+                     NULL);
 }
 
 static void server_mutate_op(grpc_call_element *elem,
@@ -741,10 +759,10 @@
 }
 
 static void got_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr,
-                                 bool success) {
+                                 grpc_error *error) {
   grpc_call_element *elem = ptr;
   call_data *calld = elem->call_data;
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     start_new_rpc(exec_ctx, elem);
   } else {
     gpr_mu_lock(&calld->mu_state);
@@ -752,7 +770,8 @@
       calld->state = ZOMBIED;
       gpr_mu_unlock(&calld->mu_state);
       grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
-      grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL);
+      grpc_exec_ctx_push(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE,
+                         NULL);
     } else if (calld->state == PENDING) {
       calld->state = ZOMBIED;
       gpr_mu_unlock(&calld->mu_state);
@@ -785,7 +804,7 @@
 }
 
 static void channel_connectivity_changed(grpc_exec_ctx *exec_ctx, void *cd,
-                                         bool iomgr_status_ignored) {
+                                         grpc_error *error) {
   channel_data *chand = cd;
   grpc_server *server = chand->server;
   if (chand->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE) {
@@ -1092,7 +1111,9 @@
   op.set_accept_stream_user_data = chand;
   op.on_connectivity_state_change = &chand->channel_connectivity_changed;
   op.connectivity_state = &chand->connectivity_state;
-  op.disconnect = gpr_atm_acq_load(&s->shutdown_flag) != 0;
+  if (gpr_atm_acq_load(&s->shutdown_flag) != 0) {
+    op.disconnect_with_error = GRPC_ERROR_CREATE("Server shutdown");
+  }
   grpc_transport_perform_op(exec_ctx, transport, &op);
 }
 
@@ -1103,7 +1124,7 @@
 }
 
 static void listener_destroy_done(grpc_exec_ctx *exec_ctx, void *s,
-                                  bool success) {
+                                  grpc_error *error) {
   grpc_server *server = s;
   gpr_mu_lock(&server->mu_global);
   server->listeners_destroyed++;
@@ -1125,8 +1146,8 @@
   gpr_mu_lock(&server->mu_global);
   grpc_cq_begin_op(cq, tag);
   if (server->shutdown_published) {
-    grpc_cq_end_op(&exec_ctx, cq, tag, 1, done_published_shutdown, NULL,
-                   gpr_malloc(sizeof(grpc_cq_completion)));
+    grpc_cq_end_op(&exec_ctx, cq, tag, GRPC_ERROR_NONE, done_published_shutdown,
+                   NULL, gpr_malloc(sizeof(grpc_cq_completion)));
     gpr_mu_unlock(&server->mu_global);
     goto done;
   }
@@ -1149,7 +1170,8 @@
 
   /* collect all unregistered then registered calls */
   gpr_mu_lock(&server->mu_call);
-  kill_pending_work_locked(&exec_ctx, server);
+  kill_pending_work_locked(&exec_ctx, server,
+                           GRPC_ERROR_CREATE("Server Shutdown"));
   gpr_mu_unlock(&server->mu_call);
 
   maybe_finish_shutdown(&exec_ctx, server);
@@ -1177,7 +1199,8 @@
   channel_broadcaster_init(server, &broadcaster);
   gpr_mu_unlock(&server->mu_global);
 
-  channel_broadcaster_shutdown(&exec_ctx, &broadcaster, 0, 1);
+  channel_broadcaster_shutdown(&exec_ctx, &broadcaster, 0,
+                               GRPC_ERROR_CREATE("Cancelling all calls"));
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
@@ -1224,13 +1247,13 @@
   request_matcher *rm = NULL;
   int request_id;
   if (gpr_atm_acq_load(&server->shutdown_flag)) {
-    fail_call(exec_ctx, server, rc);
+    fail_call(exec_ctx, server, rc, GRPC_ERROR_CREATE("Server Shutdown"));
     return GRPC_CALL_OK;
   }
   request_id = gpr_stack_lockfree_pop(server->request_freelist);
   if (request_id == -1) {
     /* out of request ids: just fail this one */
-    fail_call(exec_ctx, server, rc);
+    fail_call(exec_ctx, server, rc, GRPC_ERROR_CREATE("Server Shutdown"));
     return GRPC_CALL_OK;
   }
   switch (rc->type) {
@@ -1258,8 +1281,8 @@
         grpc_closure_init(
             &calld->kill_zombie_closure, kill_zombie,
             grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0));
-        grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true,
-                              NULL);
+        grpc_exec_ctx_push(exec_ctx, &calld->kill_zombie_closure,
+                           GRPC_ERROR_NONE, NULL);
       } else {
         GPR_ASSERT(calld->state == PENDING);
         calld->state = ACTIVATED;
@@ -1354,12 +1377,13 @@
 }
 
 static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server,
-                      requested_call *rc) {
+                      requested_call *rc, grpc_error *error) {
   *rc->call = NULL;
   rc->initial_metadata->count = 0;
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
 
   server_ref(server);
-  grpc_cq_end_op(exec_ctx, rc->cq_for_notification, rc->tag, 0,
+  grpc_cq_end_op(exec_ctx, rc->cq_for_notification, rc->tag, error,
                  done_request_event, rc, &rc->completion);
 }
 
diff --git a/src/core/lib/transport/connectivity_state.c b/src/core/lib/transport/connectivity_state.c
index e24ee63..3286af9 100644
--- a/src/core/lib/transport/connectivity_state.c
+++ b/src/core/lib/transport/connectivity_state.c
@@ -61,35 +61,40 @@
                                   grpc_connectivity_state init_state,
                                   const char *name) {
   tracker->current_state = init_state;
+  tracker->current_error = GRPC_ERROR_NONE;
   tracker->watchers = NULL;
   tracker->name = gpr_strdup(name);
 }
 
 void grpc_connectivity_state_destroy(grpc_exec_ctx *exec_ctx,
                                      grpc_connectivity_state_tracker *tracker) {
-  int success;
+  grpc_error *error;
   grpc_connectivity_state_watcher *w;
   while ((w = tracker->watchers)) {
     tracker->watchers = w->next;
 
     if (GRPC_CHANNEL_FATAL_FAILURE != *w->current) {
       *w->current = GRPC_CHANNEL_FATAL_FAILURE;
-      success = 1;
+      error = GRPC_ERROR_NONE;
     } else {
-      success = 0;
+      error = GRPC_ERROR_CREATE("Shutdown connectivity owner");
     }
-    grpc_exec_ctx_enqueue(exec_ctx, w->notify, success, NULL);
+    grpc_exec_ctx_push(exec_ctx, w->notify, error, NULL);
     gpr_free(w);
   }
+  GRPC_ERROR_UNREF(tracker->current_error);
   gpr_free(tracker->name);
 }
 
 grpc_connectivity_state grpc_connectivity_state_check(
-    grpc_connectivity_state_tracker *tracker) {
+    grpc_connectivity_state_tracker *tracker, grpc_error **error) {
   if (grpc_connectivity_state_trace) {
     gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name,
             grpc_connectivity_state_name(tracker->current_state));
   }
+  if (error != NULL) {
+    *error = GRPC_ERROR_REF(tracker->current_error);
+  }
   return tracker->current_state;
 }
 
@@ -109,7 +114,7 @@
   if (current == NULL) {
     grpc_connectivity_state_watcher *w = tracker->watchers;
     if (w != NULL && w->notify == notify) {
-      grpc_exec_ctx_enqueue(exec_ctx, notify, false, NULL);
+      grpc_exec_ctx_push(exec_ctx, notify, GRPC_ERROR_CANCELLED, NULL);
       tracker->watchers = w->next;
       gpr_free(w);
       return 0;
@@ -117,7 +122,7 @@
     while (w != NULL) {
       grpc_connectivity_state_watcher *rm_candidate = w->next;
       if (rm_candidate != NULL && rm_candidate->notify == notify) {
-        grpc_exec_ctx_enqueue(exec_ctx, notify, false, NULL);
+        grpc_exec_ctx_push(exec_ctx, notify, GRPC_ERROR_CANCELLED, NULL);
         w->next = w->next->next;
         gpr_free(rm_candidate);
         return 0;
@@ -128,7 +133,8 @@
   } else {
     if (tracker->current_state != *current) {
       *current = tracker->current_state;
-      grpc_exec_ctx_enqueue(exec_ctx, notify, true, NULL);
+      grpc_exec_ctx_push(exec_ctx, notify,
+                         GRPC_ERROR_REF(tracker->current_error), NULL);
     } else {
       grpc_connectivity_state_watcher *w = gpr_malloc(sizeof(*w));
       w->current = current;
@@ -143,13 +149,28 @@
 void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx,
                                  grpc_connectivity_state_tracker *tracker,
                                  grpc_connectivity_state state,
-                                 const char *reason) {
+                                 grpc_error *error, const char *reason) {
   grpc_connectivity_state_watcher *w;
   if (grpc_connectivity_state_trace) {
-    gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s]", tracker, tracker->name,
-            grpc_connectivity_state_name(tracker->current_state),
-            grpc_connectivity_state_name(state), reason);
+    const char *error_string = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s] error=%p %s", tracker,
+            tracker->name, grpc_connectivity_state_name(tracker->current_state),
+            grpc_connectivity_state_name(state), reason, error, error_string);
+    grpc_error_free_string(error_string);
   }
+  switch (state) {
+    case GRPC_CHANNEL_CONNECTING:
+    case GRPC_CHANNEL_IDLE:
+    case GRPC_CHANNEL_READY:
+      GPR_ASSERT(error == GRPC_ERROR_NONE);
+      break;
+    case GRPC_CHANNEL_FATAL_FAILURE:
+    case GRPC_CHANNEL_TRANSIENT_FAILURE:
+      GPR_ASSERT(error != GRPC_ERROR_NONE);
+      break;
+  }
+  GRPC_ERROR_UNREF(tracker->current_error);
+  tracker->current_error = error;
   if (tracker->current_state == state) {
     return;
   }
@@ -158,7 +179,8 @@
   while ((w = tracker->watchers) != NULL) {
     *w->current = tracker->current_state;
     tracker->watchers = w->next;
-    grpc_exec_ctx_enqueue(exec_ctx, w->notify, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, w->notify,
+                       GRPC_ERROR_REF(tracker->current_error), NULL);
     gpr_free(w);
   }
 }
diff --git a/src/core/lib/transport/connectivity_state.h b/src/core/lib/transport/connectivity_state.h
index 2eb7e09..7a2fa52 100644
--- a/src/core/lib/transport/connectivity_state.h
+++ b/src/core/lib/transport/connectivity_state.h
@@ -49,6 +49,8 @@
 typedef struct {
   /** current connectivity state */
   grpc_connectivity_state current_state;
+  /** error associated with state */
+  grpc_error *current_error;
   /** all our watchers */
   grpc_connectivity_state_watcher *watchers;
   /** a name to help debugging */
@@ -70,10 +72,11 @@
 void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx,
                                  grpc_connectivity_state_tracker *tracker,
                                  grpc_connectivity_state state,
+                                 grpc_error *associated_error,
                                  const char *reason);
 
 grpc_connectivity_state grpc_connectivity_state_check(
-    grpc_connectivity_state_tracker *tracker);
+    grpc_connectivity_state_tracker *tracker, grpc_error **current_error);
 
 /** Return 1 if the channel should start connecting, 0 otherwise.
     If current==NULL cancel notify if it is already queued (success==0 in that
diff --git a/src/core/lib/transport/transport.c b/src/core/lib/transport/transport.c
index e6d524a..fdf0f4b 100644
--- a/src/core/lib/transport/transport.c
+++ b/src/core/lib/transport/transport.c
@@ -60,7 +60,7 @@
                        grpc_stream_refcount *refcount) {
 #endif
   if (gpr_unref(&refcount->refs)) {
-    grpc_exec_ctx_enqueue(exec_ctx, &refcount->destroy, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, &refcount->destroy, GRPC_ERROR_NONE, NULL);
   }
 }
 
@@ -143,11 +143,14 @@
   return transport->vtable->get_peer(exec_ctx, transport);
 }
 
-void grpc_transport_stream_op_finish_with_failure(
-    grpc_exec_ctx *exec_ctx, grpc_transport_stream_op *op) {
-  grpc_exec_ctx_enqueue(exec_ctx, op->recv_message_ready, false, NULL);
-  grpc_exec_ctx_enqueue(exec_ctx, op->recv_initial_metadata_ready, false, NULL);
-  grpc_exec_ctx_enqueue(exec_ctx, op->on_complete, false, NULL);
+void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx,
+                                                  grpc_transport_stream_op *op,
+                                                  grpc_error *error) {
+  grpc_exec_ctx_push(exec_ctx, op->recv_message_ready, GRPC_ERROR_REF(error),
+                     NULL);
+  grpc_exec_ctx_push(exec_ctx, op->recv_initial_metadata_ready,
+                     GRPC_ERROR_REF(error), NULL);
+  grpc_exec_ctx_push(exec_ctx, op->on_complete, error, NULL);
 }
 
 void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
@@ -171,11 +174,11 @@
   grpc_closure closure;
 } close_message_data;
 
-static void free_message(grpc_exec_ctx *exec_ctx, void *p, bool iomgr_success) {
+static void free_message(grpc_exec_ctx *exec_ctx, void *p, grpc_error *error) {
   close_message_data *cmd = p;
   gpr_slice_unref(cmd->message);
   if (cmd->then_call != NULL) {
-    cmd->then_call->cb(exec_ctx, cmd->then_call->cb_arg, iomgr_success);
+    cmd->then_call->cb(exec_ctx, cmd->then_call->cb_arg, GRPC_ERROR_REF(error));
   }
   gpr_free(cmd);
 }
diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
index 482a9d1..f2d750e 100644
--- a/src/core/lib/transport/transport.h
+++ b/src/core/lib/transport/transport.h
@@ -154,7 +154,7 @@
   grpc_closure *on_connectivity_state_change;
   grpc_connectivity_state *connectivity_state;
   /** should the transport be disconnected */
-  int disconnect;
+  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 */
@@ -216,7 +216,8 @@
                                    grpc_stream *stream, void *and_free_memory);
 
 void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx,
-                                                  grpc_transport_stream_op *op);
+                                                  grpc_transport_stream_op *op,
+                                                  grpc_error *error);
 
 void grpc_transport_stream_op_add_cancellation(grpc_transport_stream_op *op,
                                                grpc_status_code status);
diff --git a/src/python/grpcio/grpc/_cython/imports.generated.c b/src/python/grpcio/grpc/_cython/imports.generated.c
index 0955147..0b4fc87 100644
--- a/src/python/grpcio/grpc/_cython/imports.generated.c
+++ b/src/python/grpcio/grpc/_cython/imports.generated.c
@@ -258,6 +258,8 @@
 gpr_avl_add_type gpr_avl_add_import;
 gpr_avl_remove_type gpr_avl_remove_import;
 gpr_avl_get_type gpr_avl_get_import;
+gpr_avl_maybe_get_type gpr_avl_maybe_get_import;
+gpr_avl_is_empty_type gpr_avl_is_empty_import;
 gpr_cmdline_create_type gpr_cmdline_create_import;
 gpr_cmdline_add_int_type gpr_cmdline_add_int_import;
 gpr_cmdline_add_flag_type gpr_cmdline_add_flag_import;
@@ -529,6 +531,8 @@
   gpr_avl_add_import = (gpr_avl_add_type) GetProcAddress(library, "gpr_avl_add");
   gpr_avl_remove_import = (gpr_avl_remove_type) GetProcAddress(library, "gpr_avl_remove");
   gpr_avl_get_import = (gpr_avl_get_type) GetProcAddress(library, "gpr_avl_get");
+  gpr_avl_maybe_get_import = (gpr_avl_maybe_get_type) GetProcAddress(library, "gpr_avl_maybe_get");
+  gpr_avl_is_empty_import = (gpr_avl_is_empty_type) GetProcAddress(library, "gpr_avl_is_empty");
   gpr_cmdline_create_import = (gpr_cmdline_create_type) GetProcAddress(library, "gpr_cmdline_create");
   gpr_cmdline_add_int_import = (gpr_cmdline_add_int_type) GetProcAddress(library, "gpr_cmdline_add_int");
   gpr_cmdline_add_flag_import = (gpr_cmdline_add_flag_type) GetProcAddress(library, "gpr_cmdline_add_flag");
diff --git a/src/python/grpcio/grpc/_cython/imports.generated.h b/src/python/grpcio/grpc/_cython/imports.generated.h
index 54c8aaa..a9cb745 100644
--- a/src/python/grpcio/grpc/_cython/imports.generated.h
+++ b/src/python/grpcio/grpc/_cython/imports.generated.h
@@ -725,6 +725,12 @@
 typedef void *(*gpr_avl_get_type)(gpr_avl avl, void *key);
 extern gpr_avl_get_type gpr_avl_get_import;
 #define gpr_avl_get gpr_avl_get_import
+typedef int(*gpr_avl_maybe_get_type)(gpr_avl avl, void *key, void **value);
+extern gpr_avl_maybe_get_type gpr_avl_maybe_get_import;
+#define gpr_avl_maybe_get gpr_avl_maybe_get_import
+typedef int(*gpr_avl_is_empty_type)(gpr_avl avl);
+extern gpr_avl_is_empty_type gpr_avl_is_empty_import;
+#define gpr_avl_is_empty gpr_avl_is_empty_import
 typedef gpr_cmdline *(*gpr_cmdline_create_type)(const char *description);
 extern gpr_cmdline_create_type gpr_cmdline_create_import;
 #define gpr_cmdline_create gpr_cmdline_create_import
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index 162191b..f6db376 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -94,6 +94,7 @@
   'src/core/lib/iomgr/endpoint.c',
   'src/core/lib/iomgr/endpoint_pair_posix.c',
   'src/core/lib/iomgr/endpoint_pair_windows.c',
+  'src/core/lib/iomgr/error.c',
   'src/core/lib/iomgr/ev_poll_and_epoll_posix.c',
   'src/core/lib/iomgr/ev_poll_posix.c',
   'src/core/lib/iomgr/ev_posix.c',
@@ -183,20 +184,29 @@
   'src/core/ext/transport/chttp2/transport/writing.c',
   'src/core/ext/transport/chttp2/alpn/alpn.c',
   'src/core/lib/http/httpcli_security_connector.c',
-  'src/core/lib/security/b64.c',
-  'src/core/lib/security/client_auth_filter.c',
-  'src/core/lib/security/credentials.c',
-  'src/core/lib/security/credentials_metadata.c',
-  'src/core/lib/security/credentials_posix.c',
-  'src/core/lib/security/credentials_win32.c',
-  'src/core/lib/security/google_default_credentials.c',
-  'src/core/lib/security/handshake.c',
-  'src/core/lib/security/json_token.c',
-  'src/core/lib/security/jwt_verifier.c',
-  'src/core/lib/security/secure_endpoint.c',
-  'src/core/lib/security/security_connector.c',
-  'src/core/lib/security/security_context.c',
-  'src/core/lib/security/server_auth_filter.c',
+  'src/core/lib/security/context/security_context.c',
+  'src/core/lib/security/credentials/composite/composite_credentials.c',
+  'src/core/lib/security/credentials/credentials.c',
+  'src/core/lib/security/credentials/credentials_metadata.c',
+  'src/core/lib/security/credentials/fake/fake_credentials.c',
+  'src/core/lib/security/credentials/google_default/credentials_posix.c',
+  'src/core/lib/security/credentials/google_default/credentials_win32.c',
+  'src/core/lib/security/credentials/google_default/google_default_credentials.c',
+  'src/core/lib/security/credentials/iam/iam_credentials.c',
+  'src/core/lib/security/credentials/jwt/json_token.c',
+  'src/core/lib/security/credentials/jwt/jwt_credentials.c',
+  'src/core/lib/security/credentials/jwt/jwt_verifier.c',
+  'src/core/lib/security/credentials/oauth2/oauth2_credentials.c',
+  'src/core/lib/security/credentials/plugin/plugin_credentials.c',
+  'src/core/lib/security/credentials/ssl/ssl_credentials.c',
+  'src/core/lib/security/transport/client_auth_filter.c',
+  'src/core/lib/security/transport/handshake.c',
+  'src/core/lib/security/transport/secure_endpoint.c',
+  'src/core/lib/security/transport/security_connector.c',
+  'src/core/lib/security/transport/server_auth_filter.c',
+  'src/core/lib/security/transport/tsi_error.c',
+  'src/core/lib/security/util/b64.c',
+  'src/core/lib/security/util/json_util.c',
   'src/core/lib/surface/init_secure.c',
   'src/core/lib/tsi/fake_transport_security.c',
   'src/core/lib/tsi/ssl_transport_security.c',
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.c b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
index cebbe8c..9d7293b 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.c
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.c
@@ -258,6 +258,8 @@
 gpr_avl_add_type gpr_avl_add_import;
 gpr_avl_remove_type gpr_avl_remove_import;
 gpr_avl_get_type gpr_avl_get_import;
+gpr_avl_maybe_get_type gpr_avl_maybe_get_import;
+gpr_avl_is_empty_type gpr_avl_is_empty_import;
 gpr_cmdline_create_type gpr_cmdline_create_import;
 gpr_cmdline_add_int_type gpr_cmdline_add_int_import;
 gpr_cmdline_add_flag_type gpr_cmdline_add_flag_import;
@@ -525,6 +527,8 @@
   gpr_avl_add_import = (gpr_avl_add_type) GetProcAddress(library, "gpr_avl_add");
   gpr_avl_remove_import = (gpr_avl_remove_type) GetProcAddress(library, "gpr_avl_remove");
   gpr_avl_get_import = (gpr_avl_get_type) GetProcAddress(library, "gpr_avl_get");
+  gpr_avl_maybe_get_import = (gpr_avl_maybe_get_type) GetProcAddress(library, "gpr_avl_maybe_get");
+  gpr_avl_is_empty_import = (gpr_avl_is_empty_type) GetProcAddress(library, "gpr_avl_is_empty");
   gpr_cmdline_create_import = (gpr_cmdline_create_type) GetProcAddress(library, "gpr_cmdline_create");
   gpr_cmdline_add_int_import = (gpr_cmdline_add_int_type) GetProcAddress(library, "gpr_cmdline_add_int");
   gpr_cmdline_add_flag_import = (gpr_cmdline_add_flag_type) GetProcAddress(library, "gpr_cmdline_add_flag");
diff --git a/src/ruby/ext/grpc/rb_grpc_imports.generated.h b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
index d7ea6c5..7d816e1 100644
--- a/src/ruby/ext/grpc/rb_grpc_imports.generated.h
+++ b/src/ruby/ext/grpc/rb_grpc_imports.generated.h
@@ -725,6 +725,12 @@
 typedef void *(*gpr_avl_get_type)(gpr_avl avl, void *key);
 extern gpr_avl_get_type gpr_avl_get_import;
 #define gpr_avl_get gpr_avl_get_import
+typedef int(*gpr_avl_maybe_get_type)(gpr_avl avl, void *key, void **value);
+extern gpr_avl_maybe_get_type gpr_avl_maybe_get_import;
+#define gpr_avl_maybe_get gpr_avl_maybe_get_import
+typedef int(*gpr_avl_is_empty_type)(gpr_avl avl);
+extern gpr_avl_is_empty_type gpr_avl_is_empty_import;
+#define gpr_avl_is_empty gpr_avl_is_empty_import
 typedef gpr_cmdline *(*gpr_cmdline_create_type)(const char *description);
 extern gpr_cmdline_create_type gpr_cmdline_create_import;
 #define gpr_cmdline_create gpr_cmdline_create_import
diff --git a/test/core/bad_client/bad_client.c b/test/core/bad_client/bad_client.c
index e582068..fa70571 100644
--- a/test/core/bad_client/bad_client.c
+++ b/test/core/bad_client/bad_client.c
@@ -62,7 +62,7 @@
   gpr_event_set(&a->done_thd, (void *)1);
 }
 
-static void done_write(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void done_write(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   thd_args *a = arg;
   gpr_event_set(&a->done_write, (void *)1);
 }
@@ -81,7 +81,7 @@
   gpr_event read_done;
 } read_args;
 
-static void read_done(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void read_done(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   read_args *a = arg;
   a->validator(&a->incoming);
   gpr_event_set(&a->read_done, (void *)1);
diff --git a/test/core/channel/channel_stack_test.c b/test/core/channel/channel_stack_test.c
index 1a5594b..cba186d 100644
--- a/test/core/channel/channel_stack_test.c
+++ b/test/core/channel/channel_stack_test.c
@@ -81,12 +81,13 @@
   return gpr_strdup("peer");
 }
 
-static void free_channel(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void free_channel(grpc_exec_ctx *exec_ctx, void *arg,
+                         grpc_error *error) {
   grpc_channel_stack_destroy(exec_ctx, arg);
   gpr_free(arg);
 }
 
-static void free_call(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void free_call(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   grpc_call_stack_destroy(exec_ctx, arg, NULL);
   gpr_free(arg);
 }
diff --git a/test/core/client_config/resolvers/dns_resolver_connectivity_test.c b/test/core/client_config/resolvers/dns_resolver_connectivity_test.c
index 2322aa6..69c07d8 100644
--- a/test/core/client_config/resolvers/dns_resolver_connectivity_test.c
+++ b/test/core/client_config/resolvers/dns_resolver_connectivity_test.c
@@ -67,21 +67,21 @@
 static gpr_mu g_mu;
 static bool g_fail_resolution = true;
 
-static grpc_resolved_addresses *my_resolve_address(const char *name,
-                                                   const char *addr) {
+static grpc_error *my_resolve_address(const char *name, const char *addr,
+                                      grpc_resolved_addresses **addrs) {
   gpr_mu_lock(&g_mu);
   GPR_ASSERT(0 == strcmp("test", name));
   if (g_fail_resolution) {
     g_fail_resolution = false;
     gpr_mu_unlock(&g_mu);
-    return NULL;
+    return GRPC_ERROR_CREATE("Forced Failure");
   } else {
     gpr_mu_unlock(&g_mu);
-    grpc_resolved_addresses *addrs = gpr_malloc(sizeof(*addrs));
-    addrs->naddrs = 1;
-    addrs->addrs = gpr_malloc(sizeof(*addrs->addrs));
-    addrs->addrs[0].len = 123;
-    return addrs;
+    *addrs = gpr_malloc(sizeof(**addrs));
+    (*addrs)->naddrs = 1;
+    (*addrs)->addrs = gpr_malloc(sizeof(*(*addrs)->addrs));
+    (*addrs)->addrs[0].len = 123;
+    return GRPC_ERROR_NONE;
   }
 }
 
@@ -100,7 +100,7 @@
   return resolver;
 }
 
-static void on_done(grpc_exec_ctx *exec_ctx, void *ev, bool success) {
+static void on_done(grpc_exec_ctx *exec_ctx, void *ev, grpc_error *error) {
   gpr_event_set(ev, (void *)1);
 }
 
diff --git a/test/core/client_config/set_initial_connect_string_test.c b/test/core/client_config/set_initial_connect_string_test.c
index c1b8452..1d2c26d 100644
--- a/test/core/client_config/set_initial_connect_string_test.c
+++ b/test/core/client_config/set_initial_connect_string_test.c
@@ -41,7 +41,7 @@
 
 #include "src/core/ext/client_config/initial_connect_string.h"
 #include "src/core/lib/iomgr/sockaddr.h"
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/fake/fake_credentials.h"
 #include "src/core/lib/support/string.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
@@ -65,8 +65,8 @@
 static struct rpc_state state;
 static grpc_closure on_read;
 
-static void handle_read(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
-  GPR_ASSERT(success);
+static void handle_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   gpr_slice_buffer_move_into(&state.temp_incoming_buffer,
                              &state.incoming_buffer);
   if (state.incoming_buffer.length > strlen(magic_connect_string)) {
diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c
index 202fb3b..9030ef5 100644
--- a/test/core/end2end/dualstack_socket_test.c
+++ b/test/core/end2end/dualstack_socket_test.c
@@ -271,7 +271,9 @@
 }
 
 int external_dns_works(const char *host) {
-  grpc_resolved_addresses *res = grpc_blocking_resolve_address(host, "80");
+  grpc_resolved_addresses *res;
+  grpc_error *error = grpc_blocking_resolve_address(host, "80", &res);
+  GRPC_ERROR_UNREF(error);
   if (res != NULL) {
     grpc_resolved_addresses_destroy(res);
     return 1;
diff --git a/test/core/end2end/fixtures/h2_fakesec.c b/test/core/end2end/fixtures/h2_fakesec.c
index 246619b..44408b2 100644
--- a/test/core/end2end/fixtures/h2_fakesec.c
+++ b/test/core/end2end/fixtures/h2_fakesec.c
@@ -40,7 +40,7 @@
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/fake/fake_credentials.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
diff --git a/test/core/end2end/fixtures/h2_oauth2.c b/test/core/end2end/fixtures/h2_oauth2.c
index 550ff33..fc56998 100644
--- a/test/core/end2end/fixtures/h2_oauth2.c
+++ b/test/core/end2end/fixtures/h2_oauth2.c
@@ -41,7 +41,7 @@
 #include <grpc/support/log.h>
 #include "src/core/lib/channel/channel_args.h"
 #include "src/core/lib/iomgr/iomgr.h"
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 #include "test/core/end2end/data/ssl_test_data.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
diff --git a/test/core/end2end/fixtures/h2_ssl.c b/test/core/end2end/fixtures/h2_ssl.c
index 69f7616..eb28623 100644
--- a/test/core/end2end/fixtures/h2_ssl.c
+++ b/test/core/end2end/fixtures/h2_ssl.c
@@ -41,7 +41,7 @@
 #include <grpc/support/log.h>
 
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/support/tmpfile.h"
diff --git a/test/core/end2end/fixtures/h2_ssl_cert.c b/test/core/end2end/fixtures/h2_ssl_cert.c
index f4dea2c..0fa5251 100644
--- a/test/core/end2end/fixtures/h2_ssl_cert.c
+++ b/test/core/end2end/fixtures/h2_ssl_cert.c
@@ -41,7 +41,7 @@
 #include <grpc/support/log.h>
 
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/support/tmpfile.h"
diff --git a/test/core/end2end/fixtures/h2_ssl_proxy.c b/test/core/end2end/fixtures/h2_ssl_proxy.c
index 151a86c..238e6bc 100644
--- a/test/core/end2end/fixtures/h2_ssl_proxy.c
+++ b/test/core/end2end/fixtures/h2_ssl_proxy.c
@@ -41,7 +41,7 @@
 #include <grpc/support/log.h>
 
 #include "src/core/lib/channel/channel_args.h"
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/support/tmpfile.h"
diff --git a/test/core/end2end/fuzzers/api_fuzzer.c b/test/core/end2end/fuzzers/api_fuzzer.c
index b133a94..4db158a 100644
--- a/test/core/end2end/fuzzers/api_fuzzer.c
+++ b/test/core/end2end/fuzzers/api_fuzzer.c
@@ -50,7 +50,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // logging
 
-static const bool squelch = true;
+static const bool squelch = !true;
 
 static void dont_log(gpr_log_func_args *args) {}
 
@@ -186,21 +186,25 @@
 typedef struct addr_req {
   grpc_timer timer;
   char *addr;
-  grpc_resolve_cb cb;
-  void *arg;
+  grpc_closure *on_done;
+  grpc_resolved_addresses **addrs;
 } addr_req;
 
-static void finish_resolve(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void finish_resolve(grpc_exec_ctx *exec_ctx, void *arg,
+                           grpc_error *error) {
   addr_req *r = arg;
 
-  if (success && 0 == strcmp(r->addr, "server")) {
+  if (error == GRPC_ERROR_NONE && 0 == strcmp(r->addr, "server")) {
     grpc_resolved_addresses *addrs = gpr_malloc(sizeof(*addrs));
     addrs->naddrs = 1;
     addrs->addrs = gpr_malloc(sizeof(*addrs->addrs));
     addrs->addrs[0].len = 0;
-    r->cb(exec_ctx, r->arg, addrs);
+    *r->addrs = addrs;
+    grpc_exec_ctx_push(exec_ctx, r->on_done, GRPC_ERROR_NONE, NULL);
   } else {
-    r->cb(exec_ctx, r->arg, NULL);
+    grpc_exec_ctx_push(
+        exec_ctx, r->on_done,
+        GRPC_ERROR_CREATE_REFERENCING("Resolution failed", &error, 1), NULL);
   }
 
   gpr_free(r->addr);
@@ -208,12 +212,12 @@
 }
 
 void my_resolve_address(grpc_exec_ctx *exec_ctx, const char *addr,
-                        const char *default_port, grpc_resolve_cb cb,
-                        void *arg) {
+                        const char *default_port, grpc_closure *on_done,
+                        grpc_resolved_addresses **addresses) {
   addr_req *r = gpr_malloc(sizeof(*r));
   r->addr = gpr_strdup(addr);
-  r->cb = cb;
-  r->arg = arg;
+  r->on_done = on_done;
+  r->addrs = addresses;
   grpc_timer_init(exec_ctx, &r->timer,
                   gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
                                gpr_time_from_seconds(1, GPR_TIMESPAN)),
@@ -239,11 +243,11 @@
   gpr_timespec deadline;
 } future_connect;
 
-static void do_connect(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void do_connect(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   future_connect *fc = arg;
-  if (!success) {
+  if (error != GRPC_ERROR_NONE) {
     *fc->ep = NULL;
-    grpc_exec_ctx_enqueue(exec_ctx, fc->closure, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, fc->closure, GRPC_ERROR_REF(error), NULL);
   } else if (g_server != NULL) {
     grpc_endpoint *client;
     grpc_endpoint *server;
@@ -255,7 +259,7 @@
     grpc_server_setup_transport(exec_ctx, g_server, transport, NULL);
     grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0);
 
-    grpc_exec_ctx_enqueue(exec_ctx, fc->closure, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, fc->closure, GRPC_ERROR_NONE, NULL);
   } else {
     sched_connect(exec_ctx, fc->closure, fc->ep, fc->deadline);
   }
@@ -266,7 +270,8 @@
                           grpc_endpoint **ep, gpr_timespec deadline) {
   if (gpr_time_cmp(deadline, gpr_now(deadline.clock_type)) < 0) {
     *ep = NULL;
-    grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, closure,
+                       GRPC_ERROR_CREATE("Connect deadline exceeded"), NULL);
     return;
   }
 
diff --git a/test/core/end2end/fuzzers/client_fuzzer.c b/test/core/end2end/fuzzers/client_fuzzer.c
index afcf763..f6bb107 100644
--- a/test/core/end2end/fuzzers/client_fuzzer.c
+++ b/test/core/end2end/fuzzers/client_fuzzer.c
@@ -39,7 +39,7 @@
 #include "test/core/util/memory_counters.h"
 #include "test/core/util/mock_endpoint.h"
 
-static const bool squelch = true;
+static const bool squelch = !true;
 
 static void discard_write(gpr_slice slice) {}
 
@@ -49,9 +49,9 @@
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
   grpc_test_only_set_metadata_hash_seed(0);
-  struct grpc_memory_counters counters;
+  // struct grpc_memory_counters counters;
   if (squelch) gpr_set_log_function(dont_log);
-  grpc_memory_counters_init();
+  // grpc_memory_counters_init();
   grpc_init();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
@@ -153,8 +153,8 @@
     grpc_byte_buffer_destroy(response_payload_recv);
   }
   grpc_shutdown();
-  counters = grpc_memory_counters_snapshot();
-  grpc_memory_counters_destroy();
-  GPR_ASSERT(counters.total_size_relative == 0);
+  // counters = grpc_memory_counters_snapshot();
+  // grpc_memory_counters_destroy();
+  // GPR_ASSERT(counters.total_size_relative == 0);
   return 0;
 }
diff --git a/test/core/end2end/fuzzers/server_fuzzer.c b/test/core/end2end/fuzzers/server_fuzzer.c
index 4027371..d52e6cb 100644
--- a/test/core/end2end/fuzzers/server_fuzzer.c
+++ b/test/core/end2end/fuzzers/server_fuzzer.c
@@ -38,7 +38,7 @@
 #include "test/core/util/memory_counters.h"
 #include "test/core/util/mock_endpoint.h"
 
-static const bool squelch = true;
+static const bool squelch = !true;
 
 static void discard_write(gpr_slice slice) {}
 
@@ -49,9 +49,9 @@
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
   grpc_test_only_set_metadata_hash_seed(0);
-  struct grpc_memory_counters counters;
+  // struct grpc_memory_counters counters;
   if (squelch) gpr_set_log_function(dont_log);
-  grpc_memory_counters_init();
+  // grpc_memory_counters_init();
   grpc_init();
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
 
@@ -120,8 +120,8 @@
   grpc_server_destroy(server);
   grpc_completion_queue_destroy(cq);
   grpc_shutdown();
-  counters = grpc_memory_counters_snapshot();
-  grpc_memory_counters_destroy();
-  GPR_ASSERT(counters.total_size_relative == 0);
+  // counters = grpc_memory_counters_snapshot();
+  // grpc_memory_counters_destroy();
+  // GPR_ASSERT(counters.total_size_relative == 0);
   return 0;
 }
diff --git a/test/core/end2end/goaway_server_test.c b/test/core/end2end/goaway_server_test.c
index 5f8c264..ada52f7 100644
--- a/test/core/end2end/goaway_server_test.c
+++ b/test/core/end2end/goaway_server_test.c
@@ -46,8 +46,9 @@
 
 static gpr_mu g_mu;
 static int g_resolve_port = -1;
-static grpc_resolved_addresses *(*iomgr_resolve_address)(
-    const char *name, const char *default_port);
+static grpc_error *(*iomgr_resolve_address)(const char *name,
+                                            const char *default_port,
+                                            grpc_resolved_addresses **addrs);
 
 static void set_resolve_port(int port) {
   gpr_mu_lock(&g_mu);
@@ -55,28 +56,28 @@
   gpr_mu_unlock(&g_mu);
 }
 
-static grpc_resolved_addresses *my_resolve_address(const char *name,
-                                                   const char *addr) {
+static grpc_error *my_resolve_address(const char *name, const char *addr,
+                                      grpc_resolved_addresses **addrs) {
   if (0 != strcmp(name, "test")) {
-    return iomgr_resolve_address(name, addr);
+    return iomgr_resolve_address(name, addr, addrs);
   }
 
   gpr_mu_lock(&g_mu);
   if (g_resolve_port < 0) {
     gpr_mu_unlock(&g_mu);
-    return NULL;
+    return GRPC_ERROR_CREATE("Forced Failure");
   } else {
-    grpc_resolved_addresses *addrs = gpr_malloc(sizeof(*addrs));
-    addrs->naddrs = 1;
-    addrs->addrs = gpr_malloc(sizeof(*addrs->addrs));
-    memset(addrs->addrs, 0, sizeof(*addrs->addrs));
-    struct sockaddr_in *sa = (struct sockaddr_in *)addrs->addrs[0].addr;
+    *addrs = gpr_malloc(sizeof(**addrs));
+    (*addrs)->naddrs = 1;
+    (*addrs)->addrs = gpr_malloc(sizeof(*(*addrs)->addrs));
+    memset((*addrs)->addrs, 0, sizeof(*(*addrs)->addrs));
+    struct sockaddr_in *sa = (struct sockaddr_in *)(*addrs)->addrs[0].addr;
     sa->sin_family = AF_INET;
     sa->sin_addr.s_addr = htonl(0x7f000001);
     sa->sin_port = htons((uint16_t)g_resolve_port);
-    addrs->addrs[0].len = sizeof(*sa);
+    (*addrs)->addrs[0].len = sizeof(*sa);
     gpr_mu_unlock(&g_mu);
-    return addrs;
+    return GRPC_ERROR_NONE;
   }
 }
 
diff --git a/test/core/end2end/tests/call_creds.c b/test/core/end2end/tests/call_creds.c
index b555bea..5c6791f 100644
--- a/test/core/end2end/tests/call_creds.c
+++ b/test/core/end2end/tests/call_creds.c
@@ -42,7 +42,7 @@
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
 #include <grpc/support/useful.h>
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/support/string.h"
 #include "test/core/end2end/cq_verifier.h"
 
diff --git a/test/core/end2end/tests/filter_causes_close.c b/test/core/end2end/tests/filter_causes_close.c
index 99049aa..8fd847b 100644
--- a/test/core/end2end/tests/filter_causes_close.c
+++ b/test/core/end2end/tests/filter_causes_close.c
@@ -175,7 +175,7 @@
   cq_verify(cqv);
 
   GPR_ASSERT(status == GRPC_STATUS_PERMISSION_DENIED);
-  GPR_ASSERT(0 == strcmp(details, "Random failure that's not preventable."));
+  GPR_ASSERT(0 == strcmp(details, "Failure that's not preventable."));
 
   gpr_free(details);
   grpc_metadata_array_destroy(&initial_metadata_recv);
@@ -202,20 +202,23 @@
 
 typedef struct { uint8_t unused; } channel_data;
 
-static void recv_im_ready(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void recv_im_ready(grpc_exec_ctx *exec_ctx, void *arg,
+                          grpc_error *error) {
   grpc_call_element *elem = arg;
   call_data *calld = elem->call_data;
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     // close the stream with an error.
     gpr_slice message =
-        gpr_slice_from_copied_string("Random failure that's not preventable.");
+        gpr_slice_from_copied_string("Failure that's not preventable.");
     grpc_transport_stream_op op;
     memset(&op, 0, sizeof(op));
     grpc_transport_stream_op_add_close(&op, GRPC_STATUS_PERMISSION_DENIED,
                                        &message);
     grpc_call_next_op(exec_ctx, elem, &op);
   }
-  calld->recv_im_ready->cb(exec_ctx, calld->recv_im_ready->cb_arg, false);
+  grpc_exec_ctx_push(
+      exec_ctx, calld->recv_im_ready,
+      GRPC_ERROR_CREATE_REFERENCING("Forced call to close", &error, 1), NULL);
 }
 
 static void start_transport_stream_op(grpc_exec_ctx *exec_ctx,
diff --git a/test/core/http/httpcli_test.c b/test/core/http/httpcli_test.c
index d3a68d0..710f83a 100644
--- a/test/core/http/httpcli_test.c
+++ b/test/core/http/httpcli_test.c
@@ -54,19 +54,19 @@
   return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(seconds);
 }
 
-static void on_finish(grpc_exec_ctx *exec_ctx, void *arg,
-                      const grpc_httpcli_response *response) {
+static void on_finish(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   const char *expect =
       "<html><head><title>Hello world!</title></head>"
       "<body><p>This is a test</p></body></html>";
-  GPR_ASSERT(arg == (void *)42);
+  grpc_http_response *response = arg;
   GPR_ASSERT(response);
   GPR_ASSERT(response->status == 200);
   GPR_ASSERT(response->body_length == strlen(expect));
   GPR_ASSERT(0 == memcmp(expect, response->body, response->body_length));
   gpr_mu_lock(g_mu);
   g_done = 1;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
   gpr_mu_unlock(g_mu);
 }
 
@@ -86,19 +86,24 @@
   req.http.path = "/get";
   req.handshaker = &grpc_httpcli_plaintext;
 
+  grpc_http_response response;
+  memset(&response, 0, sizeof(response));
   grpc_httpcli_get(&exec_ctx, &g_context, g_pollset, &req, n_seconds_time(15),
-                   on_finish, (void *)42);
+                   grpc_closure_create(on_finish, &response), &response);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20));
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
   }
   gpr_mu_unlock(g_mu);
   gpr_free(host);
+  grpc_http_response_destroy(&response);
 }
 
 static void test_post(int port) {
@@ -117,22 +122,29 @@
   req.http.path = "/post";
   req.handshaker = &grpc_httpcli_plaintext;
 
+  grpc_http_response response;
+  memset(&response, 0, sizeof(response));
   grpc_httpcli_post(&exec_ctx, &g_context, g_pollset, &req, "hello", 5,
-                    n_seconds_time(15), on_finish, (void *)42);
+                    n_seconds_time(15),
+                    grpc_closure_create(on_finish, &response), &response);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20));
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
   }
   gpr_mu_unlock(g_mu);
   gpr_free(host);
+  grpc_http_response_destroy(&response);
 }
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
   grpc_pollset_destroy(p);
 }
 
diff --git a/test/core/http/httpscli_test.c b/test/core/http/httpscli_test.c
index d807336..ca24657 100644
--- a/test/core/http/httpscli_test.c
+++ b/test/core/http/httpscli_test.c
@@ -54,19 +54,19 @@
   return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(seconds);
 }
 
-static void on_finish(grpc_exec_ctx *exec_ctx, void *arg,
-                      const grpc_httpcli_response *response) {
+static void on_finish(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   const char *expect =
       "<html><head><title>Hello world!</title></head>"
       "<body><p>This is a test</p></body></html>";
-  GPR_ASSERT(arg == (void *)42);
+  grpc_http_response *response = arg;
   GPR_ASSERT(response);
   GPR_ASSERT(response->status == 200);
   GPR_ASSERT(response->body_length == strlen(expect));
   GPR_ASSERT(0 == memcmp(expect, response->body, response->body_length));
   gpr_mu_lock(g_mu);
   g_done = 1;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
   gpr_mu_unlock(g_mu);
 }
 
@@ -87,19 +87,24 @@
   req.http.path = "/get";
   req.handshaker = &grpc_httpcli_ssl;
 
+  grpc_http_response response;
+  memset(&response, 0, sizeof(response));
   grpc_httpcli_get(&exec_ctx, &g_context, g_pollset, &req, n_seconds_time(15),
-                   on_finish, (void *)42);
+                   grpc_closure_create(on_finish, &response), &response);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20));
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
   }
   gpr_mu_unlock(g_mu);
   gpr_free(host);
+  grpc_http_response_destroy(&response);
 }
 
 static void test_post(int port) {
@@ -119,22 +124,29 @@
   req.http.path = "/post";
   req.handshaker = &grpc_httpcli_ssl;
 
+  grpc_http_response response;
+  memset(&response, 0, sizeof(response));
   grpc_httpcli_post(&exec_ctx, &g_context, g_pollset, &req, "hello", 5,
-                    n_seconds_time(15), on_finish, (void *)42);
+                    n_seconds_time(15),
+                    grpc_closure_create(on_finish, &response), &response);
   gpr_mu_lock(g_mu);
   while (!g_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20));
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), n_seconds_time(20))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
   }
   gpr_mu_unlock(g_mu);
   gpr_free(host);
+  grpc_http_response_destroy(&response);
 }
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
   grpc_pollset_destroy(p);
 }
 
diff --git a/test/core/http/parser_test.c b/test/core/http/parser_test.c
index 7fdf60c..d645d28 100644
--- a/test/core/http/parser_test.c
+++ b/test/core/http/parser_test.c
@@ -44,38 +44,39 @@
 #include "test/core/util/test_config.h"
 
 static void test_request_succeeds(grpc_slice_split_mode split_mode,
-                                  char *request, char *expect_method,
+                                  char *request_text, char *expect_method,
                                   grpc_http_version expect_version,
                                   char *expect_path, char *expect_body, ...) {
   grpc_http_parser parser;
-  gpr_slice input_slice = gpr_slice_from_copied_string(request);
+  gpr_slice input_slice = gpr_slice_from_copied_string(request_text);
   size_t num_slices;
   size_t i;
   gpr_slice *slices;
   va_list args;
+  grpc_http_request request;
+  memset(&request, 0, sizeof(request));
 
   grpc_split_slices(split_mode, &input_slice, 1, &slices, &num_slices);
   gpr_slice_unref(input_slice);
 
-  grpc_http_parser_init(&parser);
+  grpc_http_parser_init(&parser, GRPC_HTTP_REQUEST, &request);
 
   for (i = 0; i < num_slices; i++) {
-    GPR_ASSERT(grpc_http_parser_parse(&parser, slices[i]));
+    GPR_ASSERT(grpc_http_parser_parse(&parser, slices[i]) == GRPC_ERROR_NONE);
     gpr_slice_unref(slices[i]);
   }
-  GPR_ASSERT(grpc_http_parser_eof(&parser));
+  GPR_ASSERT(grpc_http_parser_eof(&parser) == GRPC_ERROR_NONE);
 
   GPR_ASSERT(GRPC_HTTP_REQUEST == parser.type);
-  GPR_ASSERT(0 == strcmp(expect_method, parser.http.request.method));
-  GPR_ASSERT(0 == strcmp(expect_path, parser.http.request.path));
-  GPR_ASSERT(expect_version == parser.http.request.version);
+  GPR_ASSERT(0 == strcmp(expect_method, request.method));
+  GPR_ASSERT(0 == strcmp(expect_path, request.path));
+  GPR_ASSERT(expect_version == request.version);
 
   if (expect_body != NULL) {
-    GPR_ASSERT(strlen(expect_body) == parser.http.request.body_length);
-    GPR_ASSERT(0 == memcmp(expect_body, parser.http.request.body,
-                           parser.http.request.body_length));
+    GPR_ASSERT(strlen(expect_body) == request.body_length);
+    GPR_ASSERT(0 == memcmp(expect_body, request.body, request.body_length));
   } else {
-    GPR_ASSERT(parser.http.request.body_length == 0);
+    GPR_ASSERT(request.body_length == 0);
   }
 
   va_start(args, expect_body);
@@ -85,48 +86,50 @@
     char *expect_value;
     expect_key = va_arg(args, char *);
     if (!expect_key) break;
-    GPR_ASSERT(i < parser.http.request.hdr_count);
+    GPR_ASSERT(i < request.hdr_count);
     expect_value = va_arg(args, char *);
     GPR_ASSERT(expect_value);
-    GPR_ASSERT(0 == strcmp(expect_key, parser.http.request.hdrs[i].key));
-    GPR_ASSERT(0 == strcmp(expect_value, parser.http.request.hdrs[i].value));
+    GPR_ASSERT(0 == strcmp(expect_key, request.hdrs[i].key));
+    GPR_ASSERT(0 == strcmp(expect_value, request.hdrs[i].value));
     i++;
   }
   va_end(args);
-  GPR_ASSERT(i == parser.http.request.hdr_count);
+  GPR_ASSERT(i == request.hdr_count);
 
+  grpc_http_request_destroy(&request);
   grpc_http_parser_destroy(&parser);
   gpr_free(slices);
 }
 
-static void test_succeeds(grpc_slice_split_mode split_mode, char *response,
+static void test_succeeds(grpc_slice_split_mode split_mode, char *response_text,
                           int expect_status, char *expect_body, ...) {
   grpc_http_parser parser;
-  gpr_slice input_slice = gpr_slice_from_copied_string(response);
+  gpr_slice input_slice = gpr_slice_from_copied_string(response_text);
   size_t num_slices;
   size_t i;
   gpr_slice *slices;
   va_list args;
+  grpc_http_response response;
+  memset(&response, 0, sizeof(response));
 
   grpc_split_slices(split_mode, &input_slice, 1, &slices, &num_slices);
   gpr_slice_unref(input_slice);
 
-  grpc_http_parser_init(&parser);
+  grpc_http_parser_init(&parser, GRPC_HTTP_RESPONSE, &response);
 
   for (i = 0; i < num_slices; i++) {
-    GPR_ASSERT(grpc_http_parser_parse(&parser, slices[i]));
+    GPR_ASSERT(grpc_http_parser_parse(&parser, slices[i]) == GRPC_ERROR_NONE);
     gpr_slice_unref(slices[i]);
   }
-  GPR_ASSERT(grpc_http_parser_eof(&parser));
+  GPR_ASSERT(grpc_http_parser_eof(&parser) == GRPC_ERROR_NONE);
 
   GPR_ASSERT(GRPC_HTTP_RESPONSE == parser.type);
-  GPR_ASSERT(expect_status == parser.http.response.status);
+  GPR_ASSERT(expect_status == response.status);
   if (expect_body != NULL) {
-    GPR_ASSERT(strlen(expect_body) == parser.http.response.body_length);
-    GPR_ASSERT(0 == memcmp(expect_body, parser.http.response.body,
-                           parser.http.response.body_length));
+    GPR_ASSERT(strlen(expect_body) == response.body_length);
+    GPR_ASSERT(0 == memcmp(expect_body, response.body, response.body_length));
   } else {
-    GPR_ASSERT(parser.http.response.body_length == 0);
+    GPR_ASSERT(response.body_length == 0);
   }
 
   va_start(args, expect_body);
@@ -136,77 +139,84 @@
     char *expect_value;
     expect_key = va_arg(args, char *);
     if (!expect_key) break;
-    GPR_ASSERT(i < parser.http.response.hdr_count);
+    GPR_ASSERT(i < response.hdr_count);
     expect_value = va_arg(args, char *);
     GPR_ASSERT(expect_value);
-    GPR_ASSERT(0 == strcmp(expect_key, parser.http.response.hdrs[i].key));
-    GPR_ASSERT(0 == strcmp(expect_value, parser.http.response.hdrs[i].value));
+    GPR_ASSERT(0 == strcmp(expect_key, response.hdrs[i].key));
+    GPR_ASSERT(0 == strcmp(expect_value, response.hdrs[i].value));
     i++;
   }
   va_end(args);
-  GPR_ASSERT(i == parser.http.response.hdr_count);
+  GPR_ASSERT(i == response.hdr_count);
 
+  grpc_http_response_destroy(&response);
   grpc_http_parser_destroy(&parser);
   gpr_free(slices);
 }
 
-static void test_fails(grpc_slice_split_mode split_mode, char *response) {
+static void test_fails(grpc_slice_split_mode split_mode, char *response_text) {
   grpc_http_parser parser;
-  gpr_slice input_slice = gpr_slice_from_copied_string(response);
+  gpr_slice input_slice = gpr_slice_from_copied_string(response_text);
   size_t num_slices;
   size_t i;
   gpr_slice *slices;
-  int done = 0;
+  grpc_error *error = GRPC_ERROR_NONE;
+  grpc_http_response response;
+  memset(&response, 0, sizeof(response));
 
   grpc_split_slices(split_mode, &input_slice, 1, &slices, &num_slices);
   gpr_slice_unref(input_slice);
 
-  grpc_http_parser_init(&parser);
+  grpc_http_parser_init(&parser, GRPC_HTTP_RESPONSE, &response);
 
   for (i = 0; i < num_slices; i++) {
-    if (!done && !grpc_http_parser_parse(&parser, slices[i])) {
-      done = 1;
+    if (GRPC_ERROR_NONE == error) {
+      error = grpc_http_parser_parse(&parser, slices[i]);
     }
     gpr_slice_unref(slices[i]);
   }
-  if (!done && !grpc_http_parser_eof(&parser)) {
-    done = 1;
+  if (GRPC_ERROR_NONE == error) {
+    error = grpc_http_parser_eof(&parser);
   }
-  GPR_ASSERT(done);
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  GRPC_ERROR_UNREF(error);
 
+  grpc_http_response_destroy(&response);
   grpc_http_parser_destroy(&parser);
   gpr_free(slices);
 }
 
-static const uint8_t failed_test1[] = {
-    0x9e, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30, 0x4a,
-    0x48, 0x54, 0x54, 0x30, 0x32, 0x16, 0xa,  0x2f, 0x48, 0x20,
-    0x31, 0x2e, 0x31, 0x20, 0x32, 0x30, 0x31, 0x54, 0x54, 0xb9,
-    0x32, 0x31, 0x2e, 0x20, 0x32, 0x30, 0x20,
-};
+static void test_request_fails(grpc_slice_split_mode split_mode,
+                               char *request_text) {
+  grpc_http_parser parser;
+  gpr_slice input_slice = gpr_slice_from_copied_string(request_text);
+  size_t num_slices;
+  size_t i;
+  gpr_slice *slices;
+  grpc_error *error = GRPC_ERROR_NONE;
+  grpc_http_request request;
+  memset(&request, 0, sizeof(request));
 
-typedef struct {
-  const char *name;
-  const uint8_t *data;
-  size_t length;
-} failed_test;
+  grpc_split_slices(split_mode, &input_slice, 1, &slices, &num_slices);
+  gpr_slice_unref(input_slice);
 
-#define FAILED_TEST(name) \
-  { #name, name, sizeof(name) }
+  grpc_http_parser_init(&parser, GRPC_HTTP_REQUEST, &request);
 
-failed_test failed_tests[] = {
-    FAILED_TEST(failed_test1),
-};
+  for (i = 0; i < num_slices; i++) {
+    if (error == GRPC_ERROR_NONE) {
+      error = grpc_http_parser_parse(&parser, slices[i]);
+    }
+    gpr_slice_unref(slices[i]);
+  }
+  if (error == GRPC_ERROR_NONE) {
+    error = grpc_http_parser_eof(&parser);
+  }
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  GRPC_ERROR_UNREF(error);
 
-static void test_doesnt_crash(failed_test t) {
-  gpr_log(GPR_DEBUG, "Run previously failed test: %s", t.name);
-  grpc_http_parser p;
-  grpc_http_parser_init(&p);
-  gpr_slice slice =
-      gpr_slice_from_copied_buffer((const char *)t.data, t.length);
-  grpc_http_parser_parse(&p, slice);
-  gpr_slice_unref(slice);
-  grpc_http_parser_destroy(&p);
+  grpc_http_request_destroy(&request);
+  grpc_http_parser_destroy(&parser);
+  gpr_free(slices);
 }
 
 int main(int argc, char **argv) {
@@ -217,10 +227,6 @@
 
   grpc_test_init(argc, argv);
 
-  for (i = 0; i < GPR_ARRAY_SIZE(failed_tests); i++) {
-    test_doesnt_crash(failed_tests[i]);
-  }
-
   for (i = 0; i < GPR_ARRAY_SIZE(split_modes); i++) {
     test_succeeds(split_modes[i],
                   "HTTP/1.0 200 OK\r\n"
@@ -286,12 +292,12 @@
                "  def\r\n"
                "\r\n"
                "hello world!");
-    test_fails(split_modes[i], "GET\r\n");
-    test_fails(split_modes[i], "GET /\r\n");
-    test_fails(split_modes[i], "GET / HTTP/0.0\r\n");
-    test_fails(split_modes[i], "GET / ____/1.0\r\n");
-    test_fails(split_modes[i], "GET / HTTP/1.2\r\n");
-    test_fails(split_modes[i], "GET / HTTP/1.0\n");
+    test_request_fails(split_modes[i], "GET\r\n");
+    test_request_fails(split_modes[i], "GET /\r\n");
+    test_request_fails(split_modes[i], "GET / HTTP/0.0\r\n");
+    test_request_fails(split_modes[i], "GET / ____/1.0\r\n");
+    test_request_fails(split_modes[i], "GET / HTTP/1.2\r\n");
+    test_request_fails(split_modes[i], "GET / HTTP/1.0\n");
 
     tmp1 = gpr_malloc(2 * GRPC_HTTP_PARSER_MAX_HEADER_LENGTH);
     memset(tmp1, 'a', 2 * GRPC_HTTP_PARSER_MAX_HEADER_LENGTH - 1);
diff --git a/test/core/http/corpus/0299ca2580e4398d170c4a336e0c33eb2cd9d427 b/test/core/http/request_corpus/0299ca2580e4398d170c4a336e0c33eb2cd9d427
similarity index 100%
rename from test/core/http/corpus/0299ca2580e4398d170c4a336e0c33eb2cd9d427
rename to test/core/http/request_corpus/0299ca2580e4398d170c4a336e0c33eb2cd9d427
diff --git a/test/core/http/corpus/05e613853d64a9669ea3cf41b0de777dc24931ba b/test/core/http/request_corpus/05e613853d64a9669ea3cf41b0de777dc24931ba
similarity index 100%
rename from test/core/http/corpus/05e613853d64a9669ea3cf41b0de777dc24931ba
rename to test/core/http/request_corpus/05e613853d64a9669ea3cf41b0de777dc24931ba
diff --git a/test/core/http/corpus/069352518a1d1baa05f317c677d275cefda2ac97 b/test/core/http/request_corpus/069352518a1d1baa05f317c677d275cefda2ac97
similarity index 100%
rename from test/core/http/corpus/069352518a1d1baa05f317c677d275cefda2ac97
rename to test/core/http/request_corpus/069352518a1d1baa05f317c677d275cefda2ac97
diff --git a/test/core/http/corpus/0925527c9358b1e10ec0f0387cd99f35204d9a34 b/test/core/http/request_corpus/0925527c9358b1e10ec0f0387cd99f35204d9a34
similarity index 100%
rename from test/core/http/corpus/0925527c9358b1e10ec0f0387cd99f35204d9a34
rename to test/core/http/request_corpus/0925527c9358b1e10ec0f0387cd99f35204d9a34
diff --git a/test/core/http/corpus/0c5b7c2569410b526605e308309a7f36574e530d b/test/core/http/request_corpus/0c5b7c2569410b526605e308309a7f36574e530d
similarity index 100%
rename from test/core/http/corpus/0c5b7c2569410b526605e308309a7f36574e530d
rename to test/core/http/request_corpus/0c5b7c2569410b526605e308309a7f36574e530d
Binary files differ
diff --git a/test/core/http/corpus/0ef3d0a84360bb5ad66274f1226f5cb273ecdbcf b/test/core/http/request_corpus/0ef3d0a84360bb5ad66274f1226f5cb273ecdbcf
similarity index 100%
rename from test/core/http/corpus/0ef3d0a84360bb5ad66274f1226f5cb273ecdbcf
rename to test/core/http/request_corpus/0ef3d0a84360bb5ad66274f1226f5cb273ecdbcf
diff --git a/test/core/http/corpus/1e1273f90187fdf5df3625764245610f86af6aa4 b/test/core/http/request_corpus/1e1273f90187fdf5df3625764245610f86af6aa4
similarity index 100%
rename from test/core/http/corpus/1e1273f90187fdf5df3625764245610f86af6aa4
rename to test/core/http/request_corpus/1e1273f90187fdf5df3625764245610f86af6aa4
diff --git a/test/core/http/corpus/1fbc57d118f3733287e9a9d808bb8947b3260e55 b/test/core/http/request_corpus/1fbc57d118f3733287e9a9d808bb8947b3260e55
similarity index 100%
rename from test/core/http/corpus/1fbc57d118f3733287e9a9d808bb8947b3260e55
rename to test/core/http/request_corpus/1fbc57d118f3733287e9a9d808bb8947b3260e55
diff --git a/test/core/http/corpus/24756c396bc72894fd720092bb6f9c03e66b469f b/test/core/http/request_corpus/24756c396bc72894fd720092bb6f9c03e66b469f
similarity index 100%
rename from test/core/http/corpus/24756c396bc72894fd720092bb6f9c03e66b469f
rename to test/core/http/request_corpus/24756c396bc72894fd720092bb6f9c03e66b469f
diff --git a/test/core/http/corpus/276def41311933421ae7a9ee42e906c85b6a4d3f b/test/core/http/request_corpus/276def41311933421ae7a9ee42e906c85b6a4d3f
similarity index 100%
rename from test/core/http/corpus/276def41311933421ae7a9ee42e906c85b6a4d3f
rename to test/core/http/request_corpus/276def41311933421ae7a9ee42e906c85b6a4d3f
Binary files differ
diff --git a/test/core/http/corpus/29daa75432381937fd005cb25e314e328de6e9f9 b/test/core/http/request_corpus/29daa75432381937fd005cb25e314e328de6e9f9
similarity index 100%
rename from test/core/http/corpus/29daa75432381937fd005cb25e314e328de6e9f9
rename to test/core/http/request_corpus/29daa75432381937fd005cb25e314e328de6e9f9
diff --git a/test/core/http/corpus/2a75204bc492084ad853682f8de3fb137d5907bc b/test/core/http/request_corpus/2a75204bc492084ad853682f8de3fb137d5907bc
similarity index 100%
rename from test/core/http/corpus/2a75204bc492084ad853682f8de3fb137d5907bc
rename to test/core/http/request_corpus/2a75204bc492084ad853682f8de3fb137d5907bc
diff --git a/test/core/http/corpus/2d34ba249b755a880525cf53c665633a5e359305 b/test/core/http/request_corpus/2d34ba249b755a880525cf53c665633a5e359305
similarity index 100%
rename from test/core/http/corpus/2d34ba249b755a880525cf53c665633a5e359305
rename to test/core/http/request_corpus/2d34ba249b755a880525cf53c665633a5e359305
Binary files differ
diff --git a/test/core/http/corpus/33f4ea0c7ea27c37d8f95cfa64d282370efdafd2 b/test/core/http/request_corpus/33f4ea0c7ea27c37d8f95cfa64d282370efdafd2
similarity index 100%
rename from test/core/http/corpus/33f4ea0c7ea27c37d8f95cfa64d282370efdafd2
rename to test/core/http/request_corpus/33f4ea0c7ea27c37d8f95cfa64d282370efdafd2
diff --git a/test/core/http/corpus/35554617ea6418bd43161fe9a2c337ed82d7ec5b b/test/core/http/request_corpus/35554617ea6418bd43161fe9a2c337ed82d7ec5b
similarity index 100%
rename from test/core/http/corpus/35554617ea6418bd43161fe9a2c337ed82d7ec5b
rename to test/core/http/request_corpus/35554617ea6418bd43161fe9a2c337ed82d7ec5b
diff --git a/test/core/http/corpus/35f0c561297cfc840ddaeebb9fc61091f4eadece b/test/core/http/request_corpus/35f0c561297cfc840ddaeebb9fc61091f4eadece
similarity index 100%
rename from test/core/http/corpus/35f0c561297cfc840ddaeebb9fc61091f4eadece
rename to test/core/http/request_corpus/35f0c561297cfc840ddaeebb9fc61091f4eadece
diff --git a/test/core/http/corpus/3787bcc22ef645e665cc5f722b8a633af86de9cf b/test/core/http/request_corpus/3787bcc22ef645e665cc5f722b8a633af86de9cf
similarity index 100%
rename from test/core/http/corpus/3787bcc22ef645e665cc5f722b8a633af86de9cf
rename to test/core/http/request_corpus/3787bcc22ef645e665cc5f722b8a633af86de9cf
Binary files differ
diff --git a/test/core/http/corpus/3953688866ccb3b4f371f1a858570d6afdb6452d b/test/core/http/request_corpus/3953688866ccb3b4f371f1a858570d6afdb6452d
similarity index 100%
rename from test/core/http/corpus/3953688866ccb3b4f371f1a858570d6afdb6452d
rename to test/core/http/request_corpus/3953688866ccb3b4f371f1a858570d6afdb6452d
Binary files differ
diff --git a/test/core/http/corpus/39b19c41ba537f37511eff7727733715db432e76 b/test/core/http/request_corpus/39b19c41ba537f37511eff7727733715db432e76
similarity index 100%
rename from test/core/http/corpus/39b19c41ba537f37511eff7727733715db432e76
rename to test/core/http/request_corpus/39b19c41ba537f37511eff7727733715db432e76
diff --git a/test/core/http/corpus/3e3c4756d5e40b5aa250954cbac86b826e70a7ac b/test/core/http/request_corpus/3e3c4756d5e40b5aa250954cbac86b826e70a7ac
similarity index 100%
rename from test/core/http/corpus/3e3c4756d5e40b5aa250954cbac86b826e70a7ac
rename to test/core/http/request_corpus/3e3c4756d5e40b5aa250954cbac86b826e70a7ac
diff --git a/test/core/http/corpus/3f03265921120c6ffa61b944e213e062a5538d4b b/test/core/http/request_corpus/3f03265921120c6ffa61b944e213e062a5538d4b
similarity index 100%
rename from test/core/http/corpus/3f03265921120c6ffa61b944e213e062a5538d4b
rename to test/core/http/request_corpus/3f03265921120c6ffa61b944e213e062a5538d4b
diff --git a/test/core/http/corpus/3fb034e66ee5494a67acae1b4e6ff64ba92a2046 b/test/core/http/request_corpus/3fb034e66ee5494a67acae1b4e6ff64ba92a2046
similarity index 100%
rename from test/core/http/corpus/3fb034e66ee5494a67acae1b4e6ff64ba92a2046
rename to test/core/http/request_corpus/3fb034e66ee5494a67acae1b4e6ff64ba92a2046
diff --git a/test/core/http/corpus/466059ed07a0d55d6ad5e522c7d367cbf278eaf9 b/test/core/http/request_corpus/466059ed07a0d55d6ad5e522c7d367cbf278eaf9
similarity index 100%
rename from test/core/http/corpus/466059ed07a0d55d6ad5e522c7d367cbf278eaf9
rename to test/core/http/request_corpus/466059ed07a0d55d6ad5e522c7d367cbf278eaf9
Binary files differ
diff --git a/test/core/http/corpus/487725eb38511c79a9340bf4560a1411061fa6fa b/test/core/http/request_corpus/487725eb38511c79a9340bf4560a1411061fa6fa
similarity index 100%
rename from test/core/http/corpus/487725eb38511c79a9340bf4560a1411061fa6fa
rename to test/core/http/request_corpus/487725eb38511c79a9340bf4560a1411061fa6fa
diff --git a/test/core/http/corpus/48b9b205cae8ac21512a3f26f49fd53e21ee13c5 b/test/core/http/request_corpus/48b9b205cae8ac21512a3f26f49fd53e21ee13c5
similarity index 100%
rename from test/core/http/corpus/48b9b205cae8ac21512a3f26f49fd53e21ee13c5
rename to test/core/http/request_corpus/48b9b205cae8ac21512a3f26f49fd53e21ee13c5
diff --git a/test/core/http/corpus/4b1f1f79a0bfa3f942479dd5f8edb59a7c257c55 b/test/core/http/request_corpus/4b1f1f79a0bfa3f942479dd5f8edb59a7c257c55
similarity index 100%
rename from test/core/http/corpus/4b1f1f79a0bfa3f942479dd5f8edb59a7c257c55
rename to test/core/http/request_corpus/4b1f1f79a0bfa3f942479dd5f8edb59a7c257c55
diff --git a/test/core/http/corpus/5028c56a5116a186b7343ff59567b47347a0796d b/test/core/http/request_corpus/5028c56a5116a186b7343ff59567b47347a0796d
similarity index 100%
rename from test/core/http/corpus/5028c56a5116a186b7343ff59567b47347a0796d
rename to test/core/http/request_corpus/5028c56a5116a186b7343ff59567b47347a0796d
diff --git a/test/core/http/corpus/533f62b3f495ce704babf3ee8d840f196a714dff b/test/core/http/request_corpus/533f62b3f495ce704babf3ee8d840f196a714dff
similarity index 100%
rename from test/core/http/corpus/533f62b3f495ce704babf3ee8d840f196a714dff
rename to test/core/http/request_corpus/533f62b3f495ce704babf3ee8d840f196a714dff
diff --git a/test/core/http/corpus/5892cbb284771fc9761caae37b19cd6e27dbc104 b/test/core/http/request_corpus/5892cbb284771fc9761caae37b19cd6e27dbc104
similarity index 100%
rename from test/core/http/corpus/5892cbb284771fc9761caae37b19cd6e27dbc104
rename to test/core/http/request_corpus/5892cbb284771fc9761caae37b19cd6e27dbc104
diff --git a/test/core/http/corpus/5aeab6e4f7c2a1c09d4ac0dbdb3beac4893607ee b/test/core/http/request_corpus/5aeab6e4f7c2a1c09d4ac0dbdb3beac4893607ee
similarity index 100%
rename from test/core/http/corpus/5aeab6e4f7c2a1c09d4ac0dbdb3beac4893607ee
rename to test/core/http/request_corpus/5aeab6e4f7c2a1c09d4ac0dbdb3beac4893607ee
Binary files differ
diff --git a/test/core/http/corpus/5b6292bdf009b0daecbc90b85cca30a88c36eec5 b/test/core/http/request_corpus/5b6292bdf009b0daecbc90b85cca30a88c36eec5
similarity index 100%
rename from test/core/http/corpus/5b6292bdf009b0daecbc90b85cca30a88c36eec5
rename to test/core/http/request_corpus/5b6292bdf009b0daecbc90b85cca30a88c36eec5
diff --git a/test/core/http/corpus/5c1659b77678b41faa4fa13df7772dae3238d1c0 b/test/core/http/request_corpus/5c1659b77678b41faa4fa13df7772dae3238d1c0
similarity index 100%
rename from test/core/http/corpus/5c1659b77678b41faa4fa13df7772dae3238d1c0
rename to test/core/http/request_corpus/5c1659b77678b41faa4fa13df7772dae3238d1c0
diff --git a/test/core/http/corpus/5c81f61621e29ec9c6a64ac3af9b3b216141618e b/test/core/http/request_corpus/5c81f61621e29ec9c6a64ac3af9b3b216141618e
similarity index 100%
rename from test/core/http/corpus/5c81f61621e29ec9c6a64ac3af9b3b216141618e
rename to test/core/http/request_corpus/5c81f61621e29ec9c6a64ac3af9b3b216141618e
Binary files differ
diff --git a/test/core/http/corpus/657368df512ca6294b9df16adf935a3f374a8be2 b/test/core/http/request_corpus/657368df512ca6294b9df16adf935a3f374a8be2
similarity index 100%
rename from test/core/http/corpus/657368df512ca6294b9df16adf935a3f374a8be2
rename to test/core/http/request_corpus/657368df512ca6294b9df16adf935a3f374a8be2
diff --git a/test/core/http/corpus/7fc4520094902ce2c760d70eaad5b674d2817337 b/test/core/http/request_corpus/7fc4520094902ce2c760d70eaad5b674d2817337
similarity index 100%
rename from test/core/http/corpus/7fc4520094902ce2c760d70eaad5b674d2817337
rename to test/core/http/request_corpus/7fc4520094902ce2c760d70eaad5b674d2817337
diff --git a/test/core/http/corpus/81f59a12b458ec3604035cb962165c604d1355e6 b/test/core/http/request_corpus/81f59a12b458ec3604035cb962165c604d1355e6
similarity index 100%
rename from test/core/http/corpus/81f59a12b458ec3604035cb962165c604d1355e6
rename to test/core/http/request_corpus/81f59a12b458ec3604035cb962165c604d1355e6
diff --git a/test/core/http/corpus/8f41c50e88ee8c17ecad3d41d63d38fb12aca0b9 b/test/core/http/request_corpus/8f41c50e88ee8c17ecad3d41d63d38fb12aca0b9
similarity index 100%
rename from test/core/http/corpus/8f41c50e88ee8c17ecad3d41d63d38fb12aca0b9
rename to test/core/http/request_corpus/8f41c50e88ee8c17ecad3d41d63d38fb12aca0b9
diff --git a/test/core/http/corpus/97c16de7fe3c390a2e6c09ff5c28f17d5c67542c b/test/core/http/request_corpus/97c16de7fe3c390a2e6c09ff5c28f17d5c67542c
similarity index 100%
rename from test/core/http/corpus/97c16de7fe3c390a2e6c09ff5c28f17d5c67542c
rename to test/core/http/request_corpus/97c16de7fe3c390a2e6c09ff5c28f17d5c67542c
Binary files differ
diff --git a/test/core/http/corpus/97e4499d450c95660de86747f527e670f2012548 b/test/core/http/request_corpus/97e4499d450c95660de86747f527e670f2012548
similarity index 100%
rename from test/core/http/corpus/97e4499d450c95660de86747f527e670f2012548
rename to test/core/http/request_corpus/97e4499d450c95660de86747f527e670f2012548
diff --git a/test/core/http/corpus/9a996857196e0998a1278994a9bab3d35526e7f1 b/test/core/http/request_corpus/9a996857196e0998a1278994a9bab3d35526e7f1
similarity index 100%
rename from test/core/http/corpus/9a996857196e0998a1278994a9bab3d35526e7f1
rename to test/core/http/request_corpus/9a996857196e0998a1278994a9bab3d35526e7f1
diff --git a/test/core/http/corpus/9b7e00049ec356ecd84b1747e4e1941140139ae8 b/test/core/http/request_corpus/9b7e00049ec356ecd84b1747e4e1941140139ae8
similarity index 100%
rename from test/core/http/corpus/9b7e00049ec356ecd84b1747e4e1941140139ae8
rename to test/core/http/request_corpus/9b7e00049ec356ecd84b1747e4e1941140139ae8
diff --git a/test/core/http/corpus/9f0c38ec455cc363369b3674a2d32bc21c206de1 b/test/core/http/request_corpus/9f0c38ec455cc363369b3674a2d32bc21c206de1
similarity index 100%
rename from test/core/http/corpus/9f0c38ec455cc363369b3674a2d32bc21c206de1
rename to test/core/http/request_corpus/9f0c38ec455cc363369b3674a2d32bc21c206de1
diff --git a/test/core/http/corpus/a1dc7bc235e46eb21d91084d7b52d5ff9f45df85 b/test/core/http/request_corpus/a1dc7bc235e46eb21d91084d7b52d5ff9f45df85
similarity index 100%
rename from test/core/http/corpus/a1dc7bc235e46eb21d91084d7b52d5ff9f45df85
rename to test/core/http/request_corpus/a1dc7bc235e46eb21d91084d7b52d5ff9f45df85
diff --git a/test/core/http/corpus/aa3bbb876eafa8ad8ca4ff2eabc6dd94341d2441 b/test/core/http/request_corpus/aa3bbb876eafa8ad8ca4ff2eabc6dd94341d2441
similarity index 100%
rename from test/core/http/corpus/aa3bbb876eafa8ad8ca4ff2eabc6dd94341d2441
rename to test/core/http/request_corpus/aa3bbb876eafa8ad8ca4ff2eabc6dd94341d2441
diff --git a/test/core/http/corpus/ae8ba95d7dbe99926a8f5bfd80347fd6a4b616a0 b/test/core/http/request_corpus/ae8ba95d7dbe99926a8f5bfd80347fd6a4b616a0
similarity index 100%
rename from test/core/http/corpus/ae8ba95d7dbe99926a8f5bfd80347fd6a4b616a0
rename to test/core/http/request_corpus/ae8ba95d7dbe99926a8f5bfd80347fd6a4b616a0
Binary files differ
diff --git a/test/core/http/corpus/b04fea5c041c707db0ad9c09a81672557b52cc47 b/test/core/http/request_corpus/b04fea5c041c707db0ad9c09a81672557b52cc47
similarity index 100%
rename from test/core/http/corpus/b04fea5c041c707db0ad9c09a81672557b52cc47
rename to test/core/http/request_corpus/b04fea5c041c707db0ad9c09a81672557b52cc47
diff --git a/test/core/http/corpus/c4acff8aa2ff886f35439f72625d05002990c940 b/test/core/http/request_corpus/c4acff8aa2ff886f35439f72625d05002990c940
similarity index 100%
rename from test/core/http/corpus/c4acff8aa2ff886f35439f72625d05002990c940
rename to test/core/http/request_corpus/c4acff8aa2ff886f35439f72625d05002990c940
diff --git a/test/core/http/corpus/c55ce9995b002e88a102ae2891a71e8bacb346c8 b/test/core/http/request_corpus/c55ce9995b002e88a102ae2891a71e8bacb346c8
similarity index 100%
rename from test/core/http/corpus/c55ce9995b002e88a102ae2891a71e8bacb346c8
rename to test/core/http/request_corpus/c55ce9995b002e88a102ae2891a71e8bacb346c8
diff --git a/test/core/http/corpus/ca5a0c00b8969310acb73d15ad0d0c602f1bd0c2 b/test/core/http/request_corpus/ca5a0c00b8969310acb73d15ad0d0c602f1bd0c2
similarity index 100%
rename from test/core/http/corpus/ca5a0c00b8969310acb73d15ad0d0c602f1bd0c2
rename to test/core/http/request_corpus/ca5a0c00b8969310acb73d15ad0d0c602f1bd0c2
diff --git a/test/core/http/corpus/cce734f1b263de6994f7950e0df7bf0c81449f70 b/test/core/http/request_corpus/cce734f1b263de6994f7950e0df7bf0c81449f70
similarity index 100%
rename from test/core/http/corpus/cce734f1b263de6994f7950e0df7bf0c81449f70
rename to test/core/http/request_corpus/cce734f1b263de6994f7950e0df7bf0c81449f70
diff --git a/test/core/http/corpus/d39c8ee11a697634a09b309460c0bbd967e7effa b/test/core/http/request_corpus/d39c8ee11a697634a09b309460c0bbd967e7effa
similarity index 100%
rename from test/core/http/corpus/d39c8ee11a697634a09b309460c0bbd967e7effa
rename to test/core/http/request_corpus/d39c8ee11a697634a09b309460c0bbd967e7effa
Binary files differ
diff --git a/test/core/http/corpus/d4c3e4cf5d035596433c30eaabbd2b2925f4b453 b/test/core/http/request_corpus/d4c3e4cf5d035596433c30eaabbd2b2925f4b453
similarity index 100%
rename from test/core/http/corpus/d4c3e4cf5d035596433c30eaabbd2b2925f4b453
rename to test/core/http/request_corpus/d4c3e4cf5d035596433c30eaabbd2b2925f4b453
diff --git a/test/core/http/corpus/d51f7fcc089f269c7afecaaca51966bab5fde629 b/test/core/http/request_corpus/d51f7fcc089f269c7afecaaca51966bab5fde629
similarity index 100%
rename from test/core/http/corpus/d51f7fcc089f269c7afecaaca51966bab5fde629
rename to test/core/http/request_corpus/d51f7fcc089f269c7afecaaca51966bab5fde629
diff --git a/test/core/http/corpus/d936dad71c129cf659097dc3db64550c4dd467f4 b/test/core/http/request_corpus/d936dad71c129cf659097dc3db64550c4dd467f4
similarity index 100%
rename from test/core/http/corpus/d936dad71c129cf659097dc3db64550c4dd467f4
rename to test/core/http/request_corpus/d936dad71c129cf659097dc3db64550c4dd467f4
diff --git a/test/core/http/corpus/e275b0466a8fb8d9e0e15856e343ddc7112ae66b b/test/core/http/request_corpus/e275b0466a8fb8d9e0e15856e343ddc7112ae66b
similarity index 100%
rename from test/core/http/corpus/e275b0466a8fb8d9e0e15856e343ddc7112ae66b
rename to test/core/http/request_corpus/e275b0466a8fb8d9e0e15856e343ddc7112ae66b
diff --git a/test/core/http/corpus/e5c364b205855a2991ce07482aebb2a3a6147089 b/test/core/http/request_corpus/e5c364b205855a2991ce07482aebb2a3a6147089
similarity index 100%
rename from test/core/http/corpus/e5c364b205855a2991ce07482aebb2a3a6147089
rename to test/core/http/request_corpus/e5c364b205855a2991ce07482aebb2a3a6147089
diff --git a/test/core/http/corpus/ee2077e08c3cfccd9bd82adb574ac4fc7d429afb b/test/core/http/request_corpus/ee2077e08c3cfccd9bd82adb574ac4fc7d429afb
similarity index 100%
rename from test/core/http/corpus/ee2077e08c3cfccd9bd82adb574ac4fc7d429afb
rename to test/core/http/request_corpus/ee2077e08c3cfccd9bd82adb574ac4fc7d429afb
diff --git a/test/core/http/corpus/fc5d4b9117ba9e87388174aee4f4970bdfe8d066 b/test/core/http/request_corpus/fc5d4b9117ba9e87388174aee4f4970bdfe8d066
similarity index 100%
rename from test/core/http/corpus/fc5d4b9117ba9e87388174aee4f4970bdfe8d066
rename to test/core/http/request_corpus/fc5d4b9117ba9e87388174aee4f4970bdfe8d066
diff --git a/test/core/http/corpus/fdeb2c7daa9e7704f67e141106384e6dd0042c0b b/test/core/http/request_corpus/fdeb2c7daa9e7704f67e141106384e6dd0042c0b
similarity index 100%
rename from test/core/http/corpus/fdeb2c7daa9e7704f67e141106384e6dd0042c0b
rename to test/core/http/request_corpus/fdeb2c7daa9e7704f67e141106384e6dd0042c0b
Binary files differ
diff --git a/test/core/http/corpus/request1.txt b/test/core/http/request_corpus/request1.txt
similarity index 100%
rename from test/core/http/corpus/request1.txt
rename to test/core/http/request_corpus/request1.txt
diff --git a/test/core/http/corpus/request2.txt b/test/core/http/request_corpus/request2.txt
similarity index 100%
rename from test/core/http/corpus/request2.txt
rename to test/core/http/request_corpus/request2.txt
diff --git a/test/core/http/corpus/request3.txt b/test/core/http/request_corpus/request3.txt
similarity index 100%
rename from test/core/http/corpus/request3.txt
rename to test/core/http/request_corpus/request3.txt
diff --git a/test/core/http/corpus/request4.txt b/test/core/http/request_corpus/request4.txt
similarity index 100%
rename from test/core/http/corpus/request4.txt
rename to test/core/http/request_corpus/request4.txt
diff --git a/test/core/http/corpus/request5.txt b/test/core/http/request_corpus/request5.txt
similarity index 100%
rename from test/core/http/corpus/request5.txt
rename to test/core/http/request_corpus/request5.txt
diff --git a/test/core/http/corpus/response1.txt b/test/core/http/request_corpus/response1.txt
similarity index 100%
rename from test/core/http/corpus/response1.txt
rename to test/core/http/request_corpus/response1.txt
diff --git a/test/core/http/corpus/response2.txt b/test/core/http/request_corpus/response2.txt
similarity index 100%
rename from test/core/http/corpus/response2.txt
rename to test/core/http/request_corpus/response2.txt
diff --git a/test/core/http/corpus/response3.txt b/test/core/http/request_corpus/response3.txt
similarity index 100%
rename from test/core/http/corpus/response3.txt
rename to test/core/http/request_corpus/response3.txt
diff --git a/test/core/http/corpus/response4.txt b/test/core/http/request_corpus/response4.txt
similarity index 100%
rename from test/core/http/corpus/response4.txt
rename to test/core/http/request_corpus/response4.txt
diff --git a/test/core/http/corpus/response5.txt b/test/core/http/request_corpus/response5.txt
similarity index 100%
rename from test/core/http/corpus/response5.txt
rename to test/core/http/request_corpus/response5.txt
diff --git a/test/core/http/corpus/response6.txt b/test/core/http/request_corpus/response6.txt
similarity index 100%
rename from test/core/http/corpus/response6.txt
rename to test/core/http/request_corpus/response6.txt
diff --git a/test/core/http/corpus/toolong.txt b/test/core/http/request_corpus/toolong.txt
similarity index 100%
rename from test/core/http/corpus/toolong.txt
rename to test/core/http/request_corpus/toolong.txt
diff --git a/test/core/http/fuzzer.c b/test/core/http/request_fuzzer.c
similarity index 87%
rename from test/core/http/fuzzer.c
rename to test/core/http/request_fuzzer.c
index 7e4f4eb..aac6cbb 100644
--- a/test/core/http/fuzzer.c
+++ b/test/core/http/request_fuzzer.c
@@ -40,11 +40,14 @@
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
   grpc_http_parser parser;
-  grpc_http_parser_init(&parser);
+  grpc_http_request request;
+  memset(&request, 0, sizeof(request));
+  grpc_http_parser_init(&parser, GRPC_HTTP_REQUEST, &request);
   gpr_slice slice = gpr_slice_from_copied_buffer((const char *)data, size);
-  grpc_http_parser_parse(&parser, slice);
-  grpc_http_parser_eof(&parser);
+  GRPC_ERROR_UNREF(grpc_http_parser_parse(&parser, slice));
+  GRPC_ERROR_UNREF(grpc_http_parser_eof(&parser));
   gpr_slice_unref(slice);
   grpc_http_parser_destroy(&parser);
+  grpc_http_request_destroy(&request);
   return 0;
 }
diff --git a/test/core/http/corpus/0299ca2580e4398d170c4a336e0c33eb2cd9d427 b/test/core/http/response_corpus/0299ca2580e4398d170c4a336e0c33eb2cd9d427
similarity index 100%
copy from test/core/http/corpus/0299ca2580e4398d170c4a336e0c33eb2cd9d427
copy to test/core/http/response_corpus/0299ca2580e4398d170c4a336e0c33eb2cd9d427
diff --git a/test/core/http/corpus/05e613853d64a9669ea3cf41b0de777dc24931ba b/test/core/http/response_corpus/05e613853d64a9669ea3cf41b0de777dc24931ba
similarity index 100%
copy from test/core/http/corpus/05e613853d64a9669ea3cf41b0de777dc24931ba
copy to test/core/http/response_corpus/05e613853d64a9669ea3cf41b0de777dc24931ba
diff --git a/test/core/http/corpus/069352518a1d1baa05f317c677d275cefda2ac97 b/test/core/http/response_corpus/069352518a1d1baa05f317c677d275cefda2ac97
similarity index 100%
copy from test/core/http/corpus/069352518a1d1baa05f317c677d275cefda2ac97
copy to test/core/http/response_corpus/069352518a1d1baa05f317c677d275cefda2ac97
diff --git a/test/core/http/corpus/0925527c9358b1e10ec0f0387cd99f35204d9a34 b/test/core/http/response_corpus/0925527c9358b1e10ec0f0387cd99f35204d9a34
similarity index 100%
copy from test/core/http/corpus/0925527c9358b1e10ec0f0387cd99f35204d9a34
copy to test/core/http/response_corpus/0925527c9358b1e10ec0f0387cd99f35204d9a34
diff --git a/test/core/http/corpus/0c5b7c2569410b526605e308309a7f36574e530d b/test/core/http/response_corpus/0c5b7c2569410b526605e308309a7f36574e530d
similarity index 100%
copy from test/core/http/corpus/0c5b7c2569410b526605e308309a7f36574e530d
copy to test/core/http/response_corpus/0c5b7c2569410b526605e308309a7f36574e530d
Binary files differ
diff --git a/test/core/http/corpus/0ef3d0a84360bb5ad66274f1226f5cb273ecdbcf b/test/core/http/response_corpus/0ef3d0a84360bb5ad66274f1226f5cb273ecdbcf
similarity index 100%
copy from test/core/http/corpus/0ef3d0a84360bb5ad66274f1226f5cb273ecdbcf
copy to test/core/http/response_corpus/0ef3d0a84360bb5ad66274f1226f5cb273ecdbcf
diff --git a/test/core/http/corpus/1e1273f90187fdf5df3625764245610f86af6aa4 b/test/core/http/response_corpus/1e1273f90187fdf5df3625764245610f86af6aa4
similarity index 100%
copy from test/core/http/corpus/1e1273f90187fdf5df3625764245610f86af6aa4
copy to test/core/http/response_corpus/1e1273f90187fdf5df3625764245610f86af6aa4
diff --git a/test/core/http/corpus/1fbc57d118f3733287e9a9d808bb8947b3260e55 b/test/core/http/response_corpus/1fbc57d118f3733287e9a9d808bb8947b3260e55
similarity index 100%
copy from test/core/http/corpus/1fbc57d118f3733287e9a9d808bb8947b3260e55
copy to test/core/http/response_corpus/1fbc57d118f3733287e9a9d808bb8947b3260e55
diff --git a/test/core/http/corpus/24756c396bc72894fd720092bb6f9c03e66b469f b/test/core/http/response_corpus/24756c396bc72894fd720092bb6f9c03e66b469f
similarity index 100%
copy from test/core/http/corpus/24756c396bc72894fd720092bb6f9c03e66b469f
copy to test/core/http/response_corpus/24756c396bc72894fd720092bb6f9c03e66b469f
diff --git a/test/core/http/corpus/276def41311933421ae7a9ee42e906c85b6a4d3f b/test/core/http/response_corpus/276def41311933421ae7a9ee42e906c85b6a4d3f
similarity index 100%
copy from test/core/http/corpus/276def41311933421ae7a9ee42e906c85b6a4d3f
copy to test/core/http/response_corpus/276def41311933421ae7a9ee42e906c85b6a4d3f
Binary files differ
diff --git a/test/core/http/corpus/29daa75432381937fd005cb25e314e328de6e9f9 b/test/core/http/response_corpus/29daa75432381937fd005cb25e314e328de6e9f9
similarity index 100%
copy from test/core/http/corpus/29daa75432381937fd005cb25e314e328de6e9f9
copy to test/core/http/response_corpus/29daa75432381937fd005cb25e314e328de6e9f9
diff --git a/test/core/http/corpus/2a75204bc492084ad853682f8de3fb137d5907bc b/test/core/http/response_corpus/2a75204bc492084ad853682f8de3fb137d5907bc
similarity index 100%
copy from test/core/http/corpus/2a75204bc492084ad853682f8de3fb137d5907bc
copy to test/core/http/response_corpus/2a75204bc492084ad853682f8de3fb137d5907bc
diff --git a/test/core/http/corpus/2d34ba249b755a880525cf53c665633a5e359305 b/test/core/http/response_corpus/2d34ba249b755a880525cf53c665633a5e359305
similarity index 100%
copy from test/core/http/corpus/2d34ba249b755a880525cf53c665633a5e359305
copy to test/core/http/response_corpus/2d34ba249b755a880525cf53c665633a5e359305
Binary files differ
diff --git a/test/core/http/corpus/33f4ea0c7ea27c37d8f95cfa64d282370efdafd2 b/test/core/http/response_corpus/33f4ea0c7ea27c37d8f95cfa64d282370efdafd2
similarity index 100%
copy from test/core/http/corpus/33f4ea0c7ea27c37d8f95cfa64d282370efdafd2
copy to test/core/http/response_corpus/33f4ea0c7ea27c37d8f95cfa64d282370efdafd2
diff --git a/test/core/http/corpus/35554617ea6418bd43161fe9a2c337ed82d7ec5b b/test/core/http/response_corpus/35554617ea6418bd43161fe9a2c337ed82d7ec5b
similarity index 100%
copy from test/core/http/corpus/35554617ea6418bd43161fe9a2c337ed82d7ec5b
copy to test/core/http/response_corpus/35554617ea6418bd43161fe9a2c337ed82d7ec5b
diff --git a/test/core/http/corpus/35f0c561297cfc840ddaeebb9fc61091f4eadece b/test/core/http/response_corpus/35f0c561297cfc840ddaeebb9fc61091f4eadece
similarity index 100%
copy from test/core/http/corpus/35f0c561297cfc840ddaeebb9fc61091f4eadece
copy to test/core/http/response_corpus/35f0c561297cfc840ddaeebb9fc61091f4eadece
diff --git a/test/core/http/corpus/3787bcc22ef645e665cc5f722b8a633af86de9cf b/test/core/http/response_corpus/3787bcc22ef645e665cc5f722b8a633af86de9cf
similarity index 100%
copy from test/core/http/corpus/3787bcc22ef645e665cc5f722b8a633af86de9cf
copy to test/core/http/response_corpus/3787bcc22ef645e665cc5f722b8a633af86de9cf
Binary files differ
diff --git a/test/core/http/corpus/3953688866ccb3b4f371f1a858570d6afdb6452d b/test/core/http/response_corpus/3953688866ccb3b4f371f1a858570d6afdb6452d
similarity index 100%
copy from test/core/http/corpus/3953688866ccb3b4f371f1a858570d6afdb6452d
copy to test/core/http/response_corpus/3953688866ccb3b4f371f1a858570d6afdb6452d
Binary files differ
diff --git a/test/core/http/corpus/39b19c41ba537f37511eff7727733715db432e76 b/test/core/http/response_corpus/39b19c41ba537f37511eff7727733715db432e76
similarity index 100%
copy from test/core/http/corpus/39b19c41ba537f37511eff7727733715db432e76
copy to test/core/http/response_corpus/39b19c41ba537f37511eff7727733715db432e76
diff --git a/test/core/http/corpus/3e3c4756d5e40b5aa250954cbac86b826e70a7ac b/test/core/http/response_corpus/3e3c4756d5e40b5aa250954cbac86b826e70a7ac
similarity index 100%
copy from test/core/http/corpus/3e3c4756d5e40b5aa250954cbac86b826e70a7ac
copy to test/core/http/response_corpus/3e3c4756d5e40b5aa250954cbac86b826e70a7ac
diff --git a/test/core/http/corpus/3f03265921120c6ffa61b944e213e062a5538d4b b/test/core/http/response_corpus/3f03265921120c6ffa61b944e213e062a5538d4b
similarity index 100%
copy from test/core/http/corpus/3f03265921120c6ffa61b944e213e062a5538d4b
copy to test/core/http/response_corpus/3f03265921120c6ffa61b944e213e062a5538d4b
diff --git a/test/core/http/corpus/3fb034e66ee5494a67acae1b4e6ff64ba92a2046 b/test/core/http/response_corpus/3fb034e66ee5494a67acae1b4e6ff64ba92a2046
similarity index 100%
copy from test/core/http/corpus/3fb034e66ee5494a67acae1b4e6ff64ba92a2046
copy to test/core/http/response_corpus/3fb034e66ee5494a67acae1b4e6ff64ba92a2046
diff --git a/test/core/http/corpus/466059ed07a0d55d6ad5e522c7d367cbf278eaf9 b/test/core/http/response_corpus/466059ed07a0d55d6ad5e522c7d367cbf278eaf9
similarity index 100%
copy from test/core/http/corpus/466059ed07a0d55d6ad5e522c7d367cbf278eaf9
copy to test/core/http/response_corpus/466059ed07a0d55d6ad5e522c7d367cbf278eaf9
Binary files differ
diff --git a/test/core/http/corpus/487725eb38511c79a9340bf4560a1411061fa6fa b/test/core/http/response_corpus/487725eb38511c79a9340bf4560a1411061fa6fa
similarity index 100%
copy from test/core/http/corpus/487725eb38511c79a9340bf4560a1411061fa6fa
copy to test/core/http/response_corpus/487725eb38511c79a9340bf4560a1411061fa6fa
diff --git a/test/core/http/corpus/48b9b205cae8ac21512a3f26f49fd53e21ee13c5 b/test/core/http/response_corpus/48b9b205cae8ac21512a3f26f49fd53e21ee13c5
similarity index 100%
copy from test/core/http/corpus/48b9b205cae8ac21512a3f26f49fd53e21ee13c5
copy to test/core/http/response_corpus/48b9b205cae8ac21512a3f26f49fd53e21ee13c5
diff --git a/test/core/http/corpus/4b1f1f79a0bfa3f942479dd5f8edb59a7c257c55 b/test/core/http/response_corpus/4b1f1f79a0bfa3f942479dd5f8edb59a7c257c55
similarity index 100%
copy from test/core/http/corpus/4b1f1f79a0bfa3f942479dd5f8edb59a7c257c55
copy to test/core/http/response_corpus/4b1f1f79a0bfa3f942479dd5f8edb59a7c257c55
diff --git a/test/core/http/corpus/5028c56a5116a186b7343ff59567b47347a0796d b/test/core/http/response_corpus/5028c56a5116a186b7343ff59567b47347a0796d
similarity index 100%
copy from test/core/http/corpus/5028c56a5116a186b7343ff59567b47347a0796d
copy to test/core/http/response_corpus/5028c56a5116a186b7343ff59567b47347a0796d
diff --git a/test/core/http/corpus/533f62b3f495ce704babf3ee8d840f196a714dff b/test/core/http/response_corpus/533f62b3f495ce704babf3ee8d840f196a714dff
similarity index 100%
copy from test/core/http/corpus/533f62b3f495ce704babf3ee8d840f196a714dff
copy to test/core/http/response_corpus/533f62b3f495ce704babf3ee8d840f196a714dff
diff --git a/test/core/http/corpus/5892cbb284771fc9761caae37b19cd6e27dbc104 b/test/core/http/response_corpus/5892cbb284771fc9761caae37b19cd6e27dbc104
similarity index 100%
copy from test/core/http/corpus/5892cbb284771fc9761caae37b19cd6e27dbc104
copy to test/core/http/response_corpus/5892cbb284771fc9761caae37b19cd6e27dbc104
diff --git a/test/core/http/corpus/5aeab6e4f7c2a1c09d4ac0dbdb3beac4893607ee b/test/core/http/response_corpus/5aeab6e4f7c2a1c09d4ac0dbdb3beac4893607ee
similarity index 100%
copy from test/core/http/corpus/5aeab6e4f7c2a1c09d4ac0dbdb3beac4893607ee
copy to test/core/http/response_corpus/5aeab6e4f7c2a1c09d4ac0dbdb3beac4893607ee
Binary files differ
diff --git a/test/core/http/corpus/5b6292bdf009b0daecbc90b85cca30a88c36eec5 b/test/core/http/response_corpus/5b6292bdf009b0daecbc90b85cca30a88c36eec5
similarity index 100%
copy from test/core/http/corpus/5b6292bdf009b0daecbc90b85cca30a88c36eec5
copy to test/core/http/response_corpus/5b6292bdf009b0daecbc90b85cca30a88c36eec5
diff --git a/test/core/http/corpus/5c1659b77678b41faa4fa13df7772dae3238d1c0 b/test/core/http/response_corpus/5c1659b77678b41faa4fa13df7772dae3238d1c0
similarity index 100%
copy from test/core/http/corpus/5c1659b77678b41faa4fa13df7772dae3238d1c0
copy to test/core/http/response_corpus/5c1659b77678b41faa4fa13df7772dae3238d1c0
diff --git a/test/core/http/corpus/5c81f61621e29ec9c6a64ac3af9b3b216141618e b/test/core/http/response_corpus/5c81f61621e29ec9c6a64ac3af9b3b216141618e
similarity index 100%
copy from test/core/http/corpus/5c81f61621e29ec9c6a64ac3af9b3b216141618e
copy to test/core/http/response_corpus/5c81f61621e29ec9c6a64ac3af9b3b216141618e
Binary files differ
diff --git a/test/core/http/corpus/657368df512ca6294b9df16adf935a3f374a8be2 b/test/core/http/response_corpus/657368df512ca6294b9df16adf935a3f374a8be2
similarity index 100%
copy from test/core/http/corpus/657368df512ca6294b9df16adf935a3f374a8be2
copy to test/core/http/response_corpus/657368df512ca6294b9df16adf935a3f374a8be2
diff --git a/test/core/http/corpus/7fc4520094902ce2c760d70eaad5b674d2817337 b/test/core/http/response_corpus/7fc4520094902ce2c760d70eaad5b674d2817337
similarity index 100%
copy from test/core/http/corpus/7fc4520094902ce2c760d70eaad5b674d2817337
copy to test/core/http/response_corpus/7fc4520094902ce2c760d70eaad5b674d2817337
diff --git a/test/core/http/corpus/81f59a12b458ec3604035cb962165c604d1355e6 b/test/core/http/response_corpus/81f59a12b458ec3604035cb962165c604d1355e6
similarity index 100%
copy from test/core/http/corpus/81f59a12b458ec3604035cb962165c604d1355e6
copy to test/core/http/response_corpus/81f59a12b458ec3604035cb962165c604d1355e6
diff --git a/test/core/http/corpus/8f41c50e88ee8c17ecad3d41d63d38fb12aca0b9 b/test/core/http/response_corpus/8f41c50e88ee8c17ecad3d41d63d38fb12aca0b9
similarity index 100%
copy from test/core/http/corpus/8f41c50e88ee8c17ecad3d41d63d38fb12aca0b9
copy to test/core/http/response_corpus/8f41c50e88ee8c17ecad3d41d63d38fb12aca0b9
diff --git a/test/core/http/corpus/97c16de7fe3c390a2e6c09ff5c28f17d5c67542c b/test/core/http/response_corpus/97c16de7fe3c390a2e6c09ff5c28f17d5c67542c
similarity index 100%
copy from test/core/http/corpus/97c16de7fe3c390a2e6c09ff5c28f17d5c67542c
copy to test/core/http/response_corpus/97c16de7fe3c390a2e6c09ff5c28f17d5c67542c
Binary files differ
diff --git a/test/core/http/corpus/97e4499d450c95660de86747f527e670f2012548 b/test/core/http/response_corpus/97e4499d450c95660de86747f527e670f2012548
similarity index 100%
copy from test/core/http/corpus/97e4499d450c95660de86747f527e670f2012548
copy to test/core/http/response_corpus/97e4499d450c95660de86747f527e670f2012548
diff --git a/test/core/http/corpus/9a996857196e0998a1278994a9bab3d35526e7f1 b/test/core/http/response_corpus/9a996857196e0998a1278994a9bab3d35526e7f1
similarity index 100%
copy from test/core/http/corpus/9a996857196e0998a1278994a9bab3d35526e7f1
copy to test/core/http/response_corpus/9a996857196e0998a1278994a9bab3d35526e7f1
diff --git a/test/core/http/corpus/9b7e00049ec356ecd84b1747e4e1941140139ae8 b/test/core/http/response_corpus/9b7e00049ec356ecd84b1747e4e1941140139ae8
similarity index 100%
copy from test/core/http/corpus/9b7e00049ec356ecd84b1747e4e1941140139ae8
copy to test/core/http/response_corpus/9b7e00049ec356ecd84b1747e4e1941140139ae8
diff --git a/test/core/http/corpus/9f0c38ec455cc363369b3674a2d32bc21c206de1 b/test/core/http/response_corpus/9f0c38ec455cc363369b3674a2d32bc21c206de1
similarity index 100%
copy from test/core/http/corpus/9f0c38ec455cc363369b3674a2d32bc21c206de1
copy to test/core/http/response_corpus/9f0c38ec455cc363369b3674a2d32bc21c206de1
diff --git a/test/core/http/corpus/a1dc7bc235e46eb21d91084d7b52d5ff9f45df85 b/test/core/http/response_corpus/a1dc7bc235e46eb21d91084d7b52d5ff9f45df85
similarity index 100%
copy from test/core/http/corpus/a1dc7bc235e46eb21d91084d7b52d5ff9f45df85
copy to test/core/http/response_corpus/a1dc7bc235e46eb21d91084d7b52d5ff9f45df85
diff --git a/test/core/http/corpus/aa3bbb876eafa8ad8ca4ff2eabc6dd94341d2441 b/test/core/http/response_corpus/aa3bbb876eafa8ad8ca4ff2eabc6dd94341d2441
similarity index 100%
copy from test/core/http/corpus/aa3bbb876eafa8ad8ca4ff2eabc6dd94341d2441
copy to test/core/http/response_corpus/aa3bbb876eafa8ad8ca4ff2eabc6dd94341d2441
diff --git a/test/core/http/corpus/ae8ba95d7dbe99926a8f5bfd80347fd6a4b616a0 b/test/core/http/response_corpus/ae8ba95d7dbe99926a8f5bfd80347fd6a4b616a0
similarity index 100%
copy from test/core/http/corpus/ae8ba95d7dbe99926a8f5bfd80347fd6a4b616a0
copy to test/core/http/response_corpus/ae8ba95d7dbe99926a8f5bfd80347fd6a4b616a0
Binary files differ
diff --git a/test/core/http/corpus/b04fea5c041c707db0ad9c09a81672557b52cc47 b/test/core/http/response_corpus/b04fea5c041c707db0ad9c09a81672557b52cc47
similarity index 100%
copy from test/core/http/corpus/b04fea5c041c707db0ad9c09a81672557b52cc47
copy to test/core/http/response_corpus/b04fea5c041c707db0ad9c09a81672557b52cc47
diff --git a/test/core/http/corpus/c4acff8aa2ff886f35439f72625d05002990c940 b/test/core/http/response_corpus/c4acff8aa2ff886f35439f72625d05002990c940
similarity index 100%
copy from test/core/http/corpus/c4acff8aa2ff886f35439f72625d05002990c940
copy to test/core/http/response_corpus/c4acff8aa2ff886f35439f72625d05002990c940
diff --git a/test/core/http/corpus/c55ce9995b002e88a102ae2891a71e8bacb346c8 b/test/core/http/response_corpus/c55ce9995b002e88a102ae2891a71e8bacb346c8
similarity index 100%
copy from test/core/http/corpus/c55ce9995b002e88a102ae2891a71e8bacb346c8
copy to test/core/http/response_corpus/c55ce9995b002e88a102ae2891a71e8bacb346c8
diff --git a/test/core/http/corpus/ca5a0c00b8969310acb73d15ad0d0c602f1bd0c2 b/test/core/http/response_corpus/ca5a0c00b8969310acb73d15ad0d0c602f1bd0c2
similarity index 100%
copy from test/core/http/corpus/ca5a0c00b8969310acb73d15ad0d0c602f1bd0c2
copy to test/core/http/response_corpus/ca5a0c00b8969310acb73d15ad0d0c602f1bd0c2
diff --git a/test/core/http/corpus/cce734f1b263de6994f7950e0df7bf0c81449f70 b/test/core/http/response_corpus/cce734f1b263de6994f7950e0df7bf0c81449f70
similarity index 100%
copy from test/core/http/corpus/cce734f1b263de6994f7950e0df7bf0c81449f70
copy to test/core/http/response_corpus/cce734f1b263de6994f7950e0df7bf0c81449f70
diff --git a/test/core/http/corpus/d39c8ee11a697634a09b309460c0bbd967e7effa b/test/core/http/response_corpus/d39c8ee11a697634a09b309460c0bbd967e7effa
similarity index 100%
copy from test/core/http/corpus/d39c8ee11a697634a09b309460c0bbd967e7effa
copy to test/core/http/response_corpus/d39c8ee11a697634a09b309460c0bbd967e7effa
Binary files differ
diff --git a/test/core/http/corpus/d4c3e4cf5d035596433c30eaabbd2b2925f4b453 b/test/core/http/response_corpus/d4c3e4cf5d035596433c30eaabbd2b2925f4b453
similarity index 100%
copy from test/core/http/corpus/d4c3e4cf5d035596433c30eaabbd2b2925f4b453
copy to test/core/http/response_corpus/d4c3e4cf5d035596433c30eaabbd2b2925f4b453
diff --git a/test/core/http/corpus/d51f7fcc089f269c7afecaaca51966bab5fde629 b/test/core/http/response_corpus/d51f7fcc089f269c7afecaaca51966bab5fde629
similarity index 100%
copy from test/core/http/corpus/d51f7fcc089f269c7afecaaca51966bab5fde629
copy to test/core/http/response_corpus/d51f7fcc089f269c7afecaaca51966bab5fde629
diff --git a/test/core/http/corpus/d936dad71c129cf659097dc3db64550c4dd467f4 b/test/core/http/response_corpus/d936dad71c129cf659097dc3db64550c4dd467f4
similarity index 100%
copy from test/core/http/corpus/d936dad71c129cf659097dc3db64550c4dd467f4
copy to test/core/http/response_corpus/d936dad71c129cf659097dc3db64550c4dd467f4
diff --git a/test/core/http/corpus/e275b0466a8fb8d9e0e15856e343ddc7112ae66b b/test/core/http/response_corpus/e275b0466a8fb8d9e0e15856e343ddc7112ae66b
similarity index 100%
copy from test/core/http/corpus/e275b0466a8fb8d9e0e15856e343ddc7112ae66b
copy to test/core/http/response_corpus/e275b0466a8fb8d9e0e15856e343ddc7112ae66b
diff --git a/test/core/http/corpus/e5c364b205855a2991ce07482aebb2a3a6147089 b/test/core/http/response_corpus/e5c364b205855a2991ce07482aebb2a3a6147089
similarity index 100%
copy from test/core/http/corpus/e5c364b205855a2991ce07482aebb2a3a6147089
copy to test/core/http/response_corpus/e5c364b205855a2991ce07482aebb2a3a6147089
diff --git a/test/core/http/corpus/ee2077e08c3cfccd9bd82adb574ac4fc7d429afb b/test/core/http/response_corpus/ee2077e08c3cfccd9bd82adb574ac4fc7d429afb
similarity index 100%
copy from test/core/http/corpus/ee2077e08c3cfccd9bd82adb574ac4fc7d429afb
copy to test/core/http/response_corpus/ee2077e08c3cfccd9bd82adb574ac4fc7d429afb
diff --git a/test/core/http/corpus/fc5d4b9117ba9e87388174aee4f4970bdfe8d066 b/test/core/http/response_corpus/fc5d4b9117ba9e87388174aee4f4970bdfe8d066
similarity index 100%
copy from test/core/http/corpus/fc5d4b9117ba9e87388174aee4f4970bdfe8d066
copy to test/core/http/response_corpus/fc5d4b9117ba9e87388174aee4f4970bdfe8d066
diff --git a/test/core/http/corpus/fdeb2c7daa9e7704f67e141106384e6dd0042c0b b/test/core/http/response_corpus/fdeb2c7daa9e7704f67e141106384e6dd0042c0b
similarity index 100%
copy from test/core/http/corpus/fdeb2c7daa9e7704f67e141106384e6dd0042c0b
copy to test/core/http/response_corpus/fdeb2c7daa9e7704f67e141106384e6dd0042c0b
Binary files differ
diff --git a/test/core/http/corpus/request1.txt b/test/core/http/response_corpus/request1.txt
similarity index 100%
copy from test/core/http/corpus/request1.txt
copy to test/core/http/response_corpus/request1.txt
diff --git a/test/core/http/corpus/request2.txt b/test/core/http/response_corpus/request2.txt
similarity index 100%
copy from test/core/http/corpus/request2.txt
copy to test/core/http/response_corpus/request2.txt
diff --git a/test/core/http/corpus/request3.txt b/test/core/http/response_corpus/request3.txt
similarity index 100%
copy from test/core/http/corpus/request3.txt
copy to test/core/http/response_corpus/request3.txt
diff --git a/test/core/http/corpus/request4.txt b/test/core/http/response_corpus/request4.txt
similarity index 100%
copy from test/core/http/corpus/request4.txt
copy to test/core/http/response_corpus/request4.txt
diff --git a/test/core/http/corpus/request5.txt b/test/core/http/response_corpus/request5.txt
similarity index 100%
copy from test/core/http/corpus/request5.txt
copy to test/core/http/response_corpus/request5.txt
diff --git a/test/core/http/corpus/response1.txt b/test/core/http/response_corpus/response1.txt
similarity index 100%
copy from test/core/http/corpus/response1.txt
copy to test/core/http/response_corpus/response1.txt
diff --git a/test/core/http/corpus/response2.txt b/test/core/http/response_corpus/response2.txt
similarity index 100%
copy from test/core/http/corpus/response2.txt
copy to test/core/http/response_corpus/response2.txt
diff --git a/test/core/http/corpus/response3.txt b/test/core/http/response_corpus/response3.txt
similarity index 100%
copy from test/core/http/corpus/response3.txt
copy to test/core/http/response_corpus/response3.txt
diff --git a/test/core/http/corpus/response4.txt b/test/core/http/response_corpus/response4.txt
similarity index 100%
copy from test/core/http/corpus/response4.txt
copy to test/core/http/response_corpus/response4.txt
diff --git a/test/core/http/corpus/response5.txt b/test/core/http/response_corpus/response5.txt
similarity index 100%
copy from test/core/http/corpus/response5.txt
copy to test/core/http/response_corpus/response5.txt
diff --git a/test/core/http/corpus/response6.txt b/test/core/http/response_corpus/response6.txt
similarity index 100%
copy from test/core/http/corpus/response6.txt
copy to test/core/http/response_corpus/response6.txt
diff --git a/test/core/http/corpus/toolong.txt b/test/core/http/response_corpus/toolong.txt
similarity index 100%
copy from test/core/http/corpus/toolong.txt
copy to test/core/http/response_corpus/toolong.txt
diff --git a/test/core/http/fuzzer.c b/test/core/http/response_fuzzer.c
similarity index 86%
copy from test/core/http/fuzzer.c
copy to test/core/http/response_fuzzer.c
index 7e4f4eb..c453e1d 100644
--- a/test/core/http/fuzzer.c
+++ b/test/core/http/response_fuzzer.c
@@ -40,11 +40,14 @@
 
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
   grpc_http_parser parser;
-  grpc_http_parser_init(&parser);
+  grpc_http_response response;
+  memset(&response, 0, sizeof(response));
+  grpc_http_parser_init(&parser, GRPC_HTTP_RESPONSE, &response);
   gpr_slice slice = gpr_slice_from_copied_buffer((const char *)data, size);
-  grpc_http_parser_parse(&parser, slice);
-  grpc_http_parser_eof(&parser);
+  GRPC_ERROR_UNREF(grpc_http_parser_parse(&parser, slice));
+  GRPC_ERROR_UNREF(grpc_http_parser_eof(&parser));
   gpr_slice_unref(slice);
   grpc_http_parser_destroy(&parser);
+  grpc_http_response_destroy(&response);
   return 0;
 }
diff --git a/test/core/internal_api_canaries/iomgr.c b/test/core/internal_api_canaries/iomgr.c
index f87a80c..71a1bb1 100644
--- a/test/core/internal_api_canaries/iomgr.c
+++ b/test/core/internal_api_canaries/iomgr.c
@@ -54,7 +54,7 @@
   grpc_closure closure;
   closure.cb = NULL;
   closure.cb_arg = NULL;
-  closure.final_data = 0;
+  closure.next_data.scratch = 0;
 
   grpc_closure_list closure_list = GRPC_CLOSURE_LIST_INIT;
   closure_list.head = NULL;
@@ -65,15 +65,14 @@
   grpc_closure_create(NULL, NULL);
 
   grpc_closure_list_move(NULL, NULL);
-  grpc_closure_list_add(NULL, NULL, true);
-  bool x = grpc_closure_list_empty(closure_list);
-  grpc_closure_next(&closure);
+  grpc_closure_list_append(NULL, NULL, GRPC_ERROR_CREATE("Foo"));
+  grpc_closure_list_empty(closure_list);
 
   /* exec_ctx.h */
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   grpc_exec_ctx_flush(&exec_ctx);
   grpc_exec_ctx_finish(&exec_ctx);
-  grpc_exec_ctx_enqueue(&exec_ctx, &closure, x, NULL);
+  grpc_exec_ctx_push(&exec_ctx, &closure, GRPC_ERROR_CREATE("Foo"), NULL);
   grpc_exec_ctx_enqueue_list(&exec_ctx, &closure_list, NULL);
 
   /* endpoint.h */
@@ -95,7 +94,7 @@
 
   /* executor.h */
   grpc_executor_init();
-  grpc_executor_enqueue(&closure, x);
+  grpc_executor_push(&closure, GRPC_ERROR_CREATE("Phi"));
   grpc_executor_shutdown();
 
   /* pollset.h */
@@ -104,9 +103,10 @@
   grpc_pollset_shutdown(NULL, NULL, NULL);
   grpc_pollset_reset(NULL);
   grpc_pollset_destroy(NULL);
-  grpc_pollset_work(NULL, NULL, NULL, gpr_now(GPR_CLOCK_REALTIME),
-                    gpr_now(GPR_CLOCK_MONOTONIC));
-  grpc_pollset_kick(NULL, NULL);
+  GRPC_ERROR_UNREF(grpc_pollset_work(NULL, NULL, NULL,
+                                     gpr_now(GPR_CLOCK_REALTIME),
+                                     gpr_now(GPR_CLOCK_MONOTONIC)));
+  GRPC_ERROR_UNREF(grpc_pollset_kick(NULL, NULL));
 }
 
 int main(void) {
diff --git a/test/core/iomgr/endpoint_pair_test.c b/test/core/iomgr/endpoint_pair_test.c
index 0df94a8..99b86b6 100644
--- a/test/core/iomgr/endpoint_pair_test.c
+++ b/test/core/iomgr/endpoint_pair_test.c
@@ -64,7 +64,8 @@
     {"tcp/tcp_socketpair", create_fixture_endpoint_pair, clean_up},
 };
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
   grpc_pollset_destroy(p);
 }
 
diff --git a/test/core/iomgr/endpoint_tests.c b/test/core/iomgr/endpoint_tests.c
index 52082c3..55e7918 100644
--- a/test/core/iomgr/endpoint_tests.c
+++ b/test/core/iomgr/endpoint_tests.c
@@ -33,6 +33,7 @@
 
 #include "test/core/iomgr/endpoint_tests.h"
 
+#include <stdbool.h>
 #include <sys/types.h>
 
 #include <grpc/support/alloc.h>
@@ -128,30 +129,30 @@
 };
 
 static void read_and_write_test_read_handler(grpc_exec_ctx *exec_ctx,
-                                             void *data, bool success) {
+                                             void *data, grpc_error *error) {
   struct read_and_write_test_state *state = data;
 
   state->bytes_read += count_slices(
       state->incoming.slices, state->incoming.count, &state->current_read_data);
-  if (state->bytes_read == state->target_bytes || !success) {
+  if (state->bytes_read == state->target_bytes || error != GRPC_ERROR_NONE) {
     gpr_log(GPR_INFO, "Read handler done");
     gpr_mu_lock(g_mu);
-    state->read_done = 1 + success;
-    grpc_pollset_kick(g_pollset, NULL);
+    state->read_done = 1 + (error == GRPC_ERROR_NONE);
+    GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL));
     gpr_mu_unlock(g_mu);
-  } else if (success) {
+  } else if (error == GRPC_ERROR_NONE) {
     grpc_endpoint_read(exec_ctx, state->read_ep, &state->incoming,
                        &state->done_read);
   }
 }
 
 static void read_and_write_test_write_handler(grpc_exec_ctx *exec_ctx,
-                                              void *data, bool success) {
+                                              void *data, grpc_error *error) {
   struct read_and_write_test_state *state = data;
   gpr_slice *slices = NULL;
   size_t nslices;
 
-  if (success) {
+  if (error == GRPC_ERROR_NONE) {
     state->bytes_written += state->current_write_size;
     if (state->target_bytes - state->bytes_written <
         state->current_write_size) {
@@ -171,8 +172,8 @@
 
   gpr_log(GPR_INFO, "Write handler done");
   gpr_mu_lock(g_mu);
-  state->write_done = 1 + success;
-  grpc_pollset_kick(g_pollset, NULL);
+  state->write_done = 1 + (error == GRPC_ERROR_NONE);
+  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL));
   gpr_mu_unlock(g_mu);
 }
 
@@ -182,7 +183,7 @@
  */
 static void read_and_write_test(grpc_endpoint_test_config config,
                                 size_t num_bytes, size_t write_size,
-                                size_t slice_size, int shutdown) {
+                                size_t slice_size, bool shutdown) {
   struct read_and_write_test_state state;
   gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(20);
   grpc_endpoint_test_fixture f =
@@ -219,7 +220,7 @@
      for the first iteration as for later iterations. It does the right thing
      even when bytes_written is unsigned. */
   state.bytes_written -= state.current_write_size;
-  read_and_write_test_write_handler(&exec_ctx, &state, 1);
+  read_and_write_test_write_handler(&exec_ctx, &state, GRPC_ERROR_NONE);
   grpc_exec_ctx_finish(&exec_ctx);
 
   grpc_endpoint_read(&exec_ctx, state.read_ep, &state.incoming,
@@ -237,8 +238,10 @@
   while (!state.read_done || !state.write_done) {
     grpc_pollset_worker *worker = NULL;
     GPR_ASSERT(gpr_time_cmp(gpr_now(GPR_CLOCK_MONOTONIC), deadline) < 0);
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), deadline);
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
   }
   gpr_mu_unlock(g_mu);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -256,11 +259,11 @@
   size_t i;
   g_pollset = pollset;
   g_mu = mu;
-  read_and_write_test(config, 10000000, 100000, 8192, 0);
-  read_and_write_test(config, 1000000, 100000, 1, 0);
-  read_and_write_test(config, 100000000, 100000, 1, 1);
+  read_and_write_test(config, 10000000, 100000, 8192, false);
+  read_and_write_test(config, 1000000, 100000, 1, false);
+  read_and_write_test(config, 100000000, 100000, 1, true);
   for (i = 1; i < 1000; i = GPR_MAX(i + 1, i * 5 / 4)) {
-    read_and_write_test(config, 40320, i, i, 0);
+    read_and_write_test(config, 40320, i, i, false);
   }
   g_pollset = NULL;
 }
diff --git a/test/core/iomgr/fd_posix_test.c b/test/core/iomgr/fd_posix_test.c
index f97f337..ab5c279 100644
--- a/test/core/iomgr/fd_posix_test.c
+++ b/test/core/iomgr/fd_posix_test.c
@@ -133,14 +133,14 @@
 
 /* Called when data become readable in a session. */
 static void session_read_cb(grpc_exec_ctx *exec_ctx, void *arg, /*session */
-                            bool success) {
+                            grpc_error *error) {
   session *se = arg;
   int fd = grpc_fd_wrapped_fd(se->em_fd);
 
   ssize_t read_once = 0;
   ssize_t read_total = 0;
 
-  if (!success) {
+  if (error != GRPC_ERROR_NONE) {
     session_shutdown_cb(exec_ctx, arg, 1);
     return;
   }
@@ -185,13 +185,14 @@
 
   gpr_mu_lock(g_mu);
   sv->done = 1;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
   gpr_mu_unlock(g_mu);
 }
 
 /* Called when a new TCP connection request arrives in the listening port. */
 static void listen_cb(grpc_exec_ctx *exec_ctx, void *arg, /*=sv_arg*/
-                      bool success) {
+                      grpc_error *error) {
   server *sv = arg;
   int fd;
   int flags;
@@ -200,7 +201,7 @@
   socklen_t slen = sizeof(ss);
   grpc_fd *listen_em_fd = sv->em_fd;
 
-  if (!success) {
+  if (error != GRPC_ERROR_NONE) {
     listen_shutdown_cb(exec_ctx, arg, 1);
     return;
   }
@@ -257,9 +258,11 @@
   while (!sv->done) {
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC),
+                          gpr_inf_future(GPR_CLOCK_MONOTONIC))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -300,17 +303,18 @@
   client *cl = arg;
   grpc_fd_orphan(exec_ctx, cl->em_fd, NULL, NULL, "c");
   cl->done = 1;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
 }
 
 /* Write as much as possible, then register notify_on_write. */
 static void client_session_write(grpc_exec_ctx *exec_ctx, void *arg, /*client */
-                                 bool success) {
+                                 grpc_error *error) {
   client *cl = arg;
   int fd = grpc_fd_wrapped_fd(cl->em_fd);
   ssize_t write_once = 0;
 
-  if (!success) {
+  if (error != GRPC_ERROR_NONE) {
     gpr_mu_lock(g_mu);
     client_session_shutdown_cb(exec_ctx, arg, 1);
     gpr_mu_unlock(g_mu);
@@ -363,7 +367,7 @@
   cl->em_fd = grpc_fd_create(fd, "client");
   grpc_pollset_add_fd(exec_ctx, g_pollset, cl->em_fd);
 
-  client_session_write(exec_ctx, cl, 1);
+  client_session_write(exec_ctx, cl, GRPC_ERROR_NONE);
 }
 
 /* Wait for the signal to shutdown a client. */
@@ -372,9 +376,11 @@
   while (!cl->done) {
     grpc_pollset_worker *worker = NULL;
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC),
+                          gpr_inf_future(GPR_CLOCK_MONOTONIC))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -411,22 +417,26 @@
 void destroy_change_data(fd_change_data *fdc) {}
 
 static void first_read_callback(grpc_exec_ctx *exec_ctx,
-                                void *arg /* fd_change_data */, bool success) {
+                                void *arg /* fd_change_data */,
+                                grpc_error *error) {
   fd_change_data *fdc = arg;
 
   gpr_mu_lock(g_mu);
   fdc->cb_that_ran = first_read_callback;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
   gpr_mu_unlock(g_mu);
 }
 
 static void second_read_callback(grpc_exec_ctx *exec_ctx,
-                                 void *arg /* fd_change_data */, bool success) {
+                                 void *arg /* fd_change_data */,
+                                 grpc_error *error) {
   fd_change_data *fdc = arg;
 
   gpr_mu_lock(g_mu);
   fdc->cb_that_ran = second_read_callback;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
   gpr_mu_unlock(g_mu);
 }
 
@@ -472,9 +482,11 @@
   gpr_mu_lock(g_mu);
   while (a.cb_that_ran == NULL) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC),
+                          gpr_inf_future(GPR_CLOCK_MONOTONIC))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -496,9 +508,11 @@
   gpr_mu_lock(g_mu);
   while (b.cb_that_ran == NULL) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC),
+                          gpr_inf_future(GPR_CLOCK_MONOTONIC))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -514,7 +528,8 @@
   close(sv[1]);
 }
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
   grpc_pollset_destroy(p);
 }
 
diff --git a/test/core/iomgr/resolve_address_test.c b/test/core/iomgr/resolve_address_test.c
index c3ede18..4417d96 100644
--- a/test/core/iomgr/resolve_address_test.c
+++ b/test/core/iomgr/resolve_address_test.c
@@ -42,54 +42,74 @@
   return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(100);
 }
 
-static void must_succeed(grpc_exec_ctx *exec_ctx, void *evp,
-                         grpc_resolved_addresses *p) {
-  GPR_ASSERT(p);
-  GPR_ASSERT(p->naddrs >= 1);
-  grpc_resolved_addresses_destroy(p);
-  gpr_event_set(evp, (void *)1);
+typedef struct args_struct {
+  gpr_event ev;
+  grpc_resolved_addresses *addrs;
+} args_struct;
+
+void args_init(args_struct *args) {
+  gpr_event_init(&args->ev);
+  args->addrs = NULL;
 }
 
-static void must_fail(grpc_exec_ctx *exec_ctx, void *evp,
-                      grpc_resolved_addresses *p) {
-  GPR_ASSERT(!p);
-  gpr_event_set(evp, (void *)1);
+void args_finish(args_struct *args) {
+  GPR_ASSERT(gpr_event_wait(&args->ev, test_deadline()));
+  grpc_resolved_addresses_destroy(args->addrs);
+}
+
+static void must_succeed(grpc_exec_ctx *exec_ctx, void *argsp,
+                         grpc_error *err) {
+  args_struct *args = argsp;
+  GPR_ASSERT(err == GRPC_ERROR_NONE);
+  GPR_ASSERT(args->addrs != NULL);
+  GPR_ASSERT(args->addrs->naddrs > 0);
+  gpr_event_set(&args->ev, (void *)1);
+}
+
+static void must_fail(grpc_exec_ctx *exec_ctx, void *argsp, grpc_error *err) {
+  args_struct *args = argsp;
+  GPR_ASSERT(err != GRPC_ERROR_NONE);
+  gpr_event_set(&args->ev, (void *)1);
 }
 
 static void test_localhost(void) {
-  gpr_event ev;
-  gpr_event_init(&ev);
+  args_struct args;
+  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "localhost:1", NULL, must_succeed, &ev);
+  grpc_resolve_address(&exec_ctx, "localhost:1", NULL,
+                       grpc_closure_create(must_succeed, &args), &args.addrs);
   grpc_exec_ctx_finish(&exec_ctx);
-  GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+  args_finish(&args);
 }
 
 static void test_default_port(void) {
-  gpr_event ev;
-  gpr_event_init(&ev);
+  args_struct args;
+  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "localhost", "1", must_succeed, &ev);
+  grpc_resolve_address(&exec_ctx, "localhost", "1",
+                       grpc_closure_create(must_succeed, &args), &args.addrs);
   grpc_exec_ctx_finish(&exec_ctx);
-  GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+  args_finish(&args);
 }
 
 static void test_missing_default_port(void) {
-  gpr_event ev;
-  gpr_event_init(&ev);
+  args_struct args;
+  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "localhost", NULL, must_fail, &ev);
+  grpc_resolve_address(&exec_ctx, "localhost", NULL,
+                       grpc_closure_create(must_fail, &args), &args.addrs);
   grpc_exec_ctx_finish(&exec_ctx);
-  GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+  args_finish(&args);
 }
 
 static void test_ipv6_with_port(void) {
-  gpr_event ev;
-  gpr_event_init(&ev);
+  args_struct args;
+  args_init(&args);
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_resolve_address(&exec_ctx, "[2001:db8::1]:1", NULL, must_succeed, &ev);
+  grpc_resolve_address(&exec_ctx, "[2001:db8::1]:1", NULL,
+                       grpc_closure_create(must_succeed, &args), &args.addrs);
   grpc_exec_ctx_finish(&exec_ctx);
-  GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+  args_finish(&args);
 }
 
 static void test_ipv6_without_port(void) {
@@ -98,12 +118,13 @@
   };
   unsigned i;
   for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
-    gpr_event ev;
-    gpr_event_init(&ev);
+    args_struct args;
+    args_init(&args);
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resolve_address(&exec_ctx, kCases[i], "80", must_succeed, &ev);
+    grpc_resolve_address(&exec_ctx, kCases[i], "80",
+                         grpc_closure_create(must_succeed, &args), &args.addrs);
     grpc_exec_ctx_finish(&exec_ctx);
-    GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+    args_finish(&args);
   }
 }
 
@@ -113,12 +134,13 @@
   };
   unsigned i;
   for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
-    gpr_event ev;
-    gpr_event_init(&ev);
+    args_struct args;
+    args_init(&args);
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resolve_address(&exec_ctx, kCases[i], NULL, must_fail, &ev);
+    grpc_resolve_address(&exec_ctx, kCases[i], NULL,
+                         grpc_closure_create(must_fail, &args), &args.addrs);
     grpc_exec_ctx_finish(&exec_ctx);
-    GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+    args_finish(&args);
   }
 }
 
@@ -128,12 +150,13 @@
   };
   unsigned i;
   for (i = 0; i < sizeof(kCases) / sizeof(*kCases); i++) {
-    gpr_event ev;
-    gpr_event_init(&ev);
+    args_struct args;
+    args_init(&args);
     grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-    grpc_resolve_address(&exec_ctx, kCases[i], "1", must_fail, &ev);
+    grpc_resolve_address(&exec_ctx, kCases[i], "1",
+                         grpc_closure_create(must_fail, &args), &args.addrs);
     grpc_exec_ctx_finish(&exec_ctx);
-    GPR_ASSERT(gpr_event_wait(&ev, test_deadline()));
+    args_finish(&args);
   }
 }
 
diff --git a/test/core/iomgr/socket_utils_test.c b/test/core/iomgr/socket_utils_test.c
index 85c027a..297531c 100644
--- a/test/core/iomgr/socket_utils_test.c
+++ b/test/core/iomgr/socket_utils_test.c
@@ -47,14 +47,22 @@
   sock = socket(PF_INET, SOCK_STREAM, 0);
   GPR_ASSERT(sock > 0);
 
-  GPR_ASSERT(grpc_set_socket_nonblocking(sock, 1));
-  GPR_ASSERT(grpc_set_socket_nonblocking(sock, 0));
-  GPR_ASSERT(grpc_set_socket_cloexec(sock, 1));
-  GPR_ASSERT(grpc_set_socket_cloexec(sock, 0));
-  GPR_ASSERT(grpc_set_socket_reuse_addr(sock, 1));
-  GPR_ASSERT(grpc_set_socket_reuse_addr(sock, 0));
-  GPR_ASSERT(grpc_set_socket_low_latency(sock, 1));
-  GPR_ASSERT(grpc_set_socket_low_latency(sock, 0));
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("set_socket_nonblocking",
+                               grpc_set_socket_nonblocking(sock, 1)));
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("set_socket_nonblocking",
+                               grpc_set_socket_nonblocking(sock, 0)));
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("set_socket_cloexec",
+                               grpc_set_socket_cloexec(sock, 1)));
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("set_socket_cloexec",
+                               grpc_set_socket_cloexec(sock, 0)));
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("set_socket_reuse_addr",
+                               grpc_set_socket_reuse_addr(sock, 1)));
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("set_socket_reuse_addr",
+                               grpc_set_socket_reuse_addr(sock, 0)));
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("set_socket_low_latency",
+                               grpc_set_socket_low_latency(sock, 1)));
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("set_socket_low_latency",
+                               grpc_set_socket_low_latency(sock, 0)));
 
   close(sock);
 
diff --git a/test/core/iomgr/tcp_client_posix_test.c b/test/core/iomgr/tcp_client_posix_test.c
index 22dc936..d0c1047 100644
--- a/test/core/iomgr/tcp_client_posix_test.c
+++ b/test/core/iomgr/tcp_client_posix_test.c
@@ -63,22 +63,24 @@
 static void finish_connection() {
   gpr_mu_lock(g_mu);
   g_connections_complete++;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
   gpr_mu_unlock(g_mu);
 }
 
-static void must_succeed(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void must_succeed(grpc_exec_ctx *exec_ctx, void *arg,
+                         grpc_error *error) {
   GPR_ASSERT(g_connecting != NULL);
-  GPR_ASSERT(success);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   grpc_endpoint_shutdown(exec_ctx, g_connecting);
   grpc_endpoint_destroy(exec_ctx, g_connecting);
   g_connecting = NULL;
   finish_connection();
 }
 
-static void must_fail(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void must_fail(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
   GPR_ASSERT(g_connecting == NULL);
-  GPR_ASSERT(!success);
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
   finish_connection();
 }
 
@@ -125,9 +127,11 @@
 
   while (g_connections_complete == connections_complete_before) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5));
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC),
+                          GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_flush(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -168,7 +172,9 @@
     gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
     gpr_timespec polling_deadline = test_deadline();
     if (!grpc_timer_check(&exec_ctx, now, &polling_deadline)) {
-      grpc_pollset_work(&exec_ctx, g_pollset, &worker, now, polling_deadline);
+      GPR_ASSERT(GRPC_LOG_IF_ERROR(
+          "pollset_work", grpc_pollset_work(&exec_ctx, g_pollset, &worker, now,
+                                            polling_deadline)));
     }
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_flush(&exec_ctx);
@@ -179,7 +185,8 @@
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
   grpc_pollset_destroy(p);
 }
 
diff --git a/test/core/iomgr/tcp_posix_test.c b/test/core/iomgr/tcp_posix_test.c
index 7a98fa0..0a351f7 100644
--- a/test/core/iomgr/tcp_posix_test.c
+++ b/test/core/iomgr/tcp_posix_test.c
@@ -139,12 +139,13 @@
   return num_bytes;
 }
 
-static void read_cb(grpc_exec_ctx *exec_ctx, void *user_data, bool success) {
+static void read_cb(grpc_exec_ctx *exec_ctx, void *user_data,
+                    grpc_error *error) {
   struct read_socket_state *state = (struct read_socket_state *)user_data;
   size_t read_bytes;
   int current_data;
 
-  GPR_ASSERT(success);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
 
   gpr_mu_lock(g_mu);
   current_data = state->read_bytes % 256;
@@ -192,8 +193,10 @@
   gpr_mu_lock(g_mu);
   while (state.read_bytes < state.target_read_bytes) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), deadline);
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -238,8 +241,10 @@
   gpr_mu_lock(g_mu);
   while (state.read_bytes < state.target_read_bytes) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), deadline);
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -281,13 +286,15 @@
 }
 
 static void write_done(grpc_exec_ctx *exec_ctx,
-                       void *user_data /* write_socket_state */, bool success) {
+                       void *user_data /* write_socket_state */,
+                       grpc_error *error) {
   struct write_socket_state *state = (struct write_socket_state *)user_data;
   gpr_log(GPR_INFO, "Write done callback called");
   gpr_mu_lock(g_mu);
   gpr_log(GPR_INFO, "Signalling write done");
   state->write_done = 1;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
   gpr_mu_unlock(g_mu);
 }
 
@@ -306,9 +313,11 @@
   for (;;) {
     grpc_pollset_worker *worker = NULL;
     gpr_mu_lock(g_mu);
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      GRPC_TIMEOUT_MILLIS_TO_DEADLINE(10));
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC),
+                          GRPC_TIMEOUT_MILLIS_TO_DEADLINE(10))));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     do {
@@ -370,8 +379,10 @@
     if (state.write_done) {
       break;
     }
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), deadline);
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -384,10 +395,11 @@
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
-void on_fd_released(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+void on_fd_released(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *errors) {
   int *done = arg;
   *done = 1;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
 }
 
 /* Do a read_test, then release fd and try to read/write again. Verify that
@@ -427,8 +439,10 @@
   gpr_mu_lock(g_mu);
   while (state.read_bytes < state.target_read_bytes) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), deadline);
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(g_mu);
@@ -441,8 +455,10 @@
   gpr_mu_lock(g_mu);
   while (!fd_released_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), deadline);
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
   }
   gpr_mu_unlock(g_mu);
   GPR_ASSERT(fd_released_done == 1);
@@ -504,7 +520,8 @@
     {"tcp/tcp_socketpair", create_fixture_tcp_socketpair, clean_up},
 };
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
   grpc_pollset_destroy(p);
 }
 
diff --git a/test/core/iomgr/tcp_server_posix_test.c b/test/core/iomgr/tcp_server_posix_test.c
index 266d239..a37ff17 100644
--- a/test/core/iomgr/tcp_server_posix_test.c
+++ b/test/core/iomgr/tcp_server_posix_test.c
@@ -90,7 +90,7 @@
 }
 
 static void server_weak_ref_shutdown(grpc_exec_ctx *exec_ctx, void *arg,
-                                     bool success) {
+                                     grpc_error *error) {
   server_weak_ref *weak_ref = arg;
   weak_ref->server = NULL;
 }
@@ -120,20 +120,23 @@
   gpr_mu_lock(g_mu);
   on_connect_result_set(&g_result, acceptor);
   g_nconnects++;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
   gpr_mu_unlock(g_mu);
 }
 
 static void test_no_op(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_tcp_server *s = grpc_tcp_server_create(NULL);
+  grpc_tcp_server *s;
+  GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, &s));
   grpc_tcp_server_unref(&exec_ctx, s);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_no_op_with_start(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_tcp_server *s = grpc_tcp_server_create(NULL);
+  grpc_tcp_server *s;
+  GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, &s));
   LOG_TEST("test_no_op_with_start");
   grpc_tcp_server_start(&exec_ctx, s, NULL, 0, on_connect, NULL);
   grpc_tcp_server_unref(&exec_ctx, s);
@@ -143,13 +146,16 @@
 static void test_no_op_with_port(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   struct sockaddr_in addr;
-  grpc_tcp_server *s = grpc_tcp_server_create(NULL);
+  grpc_tcp_server *s;
+  GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, &s));
   LOG_TEST("test_no_op_with_port");
 
   memset(&addr, 0, sizeof(addr));
   addr.sin_family = AF_INET;
-  GPR_ASSERT(
-      grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, sizeof(addr)) > 0);
+  int port;
+  GPR_ASSERT(grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, sizeof(addr),
+                                      &port) == GRPC_ERROR_NONE &&
+             port > 0);
 
   grpc_tcp_server_unref(&exec_ctx, s);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -158,13 +164,16 @@
 static void test_no_op_with_port_and_start(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   struct sockaddr_in addr;
-  grpc_tcp_server *s = grpc_tcp_server_create(NULL);
+  grpc_tcp_server *s;
+  GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, &s));
   LOG_TEST("test_no_op_with_port_and_start");
+  int port;
 
   memset(&addr, 0, sizeof(addr));
   addr.sin_family = AF_INET;
-  GPR_ASSERT(
-      grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, sizeof(addr)) > 0);
+  GPR_ASSERT(grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, sizeof(addr),
+                                      &port) == GRPC_ERROR_NONE &&
+             port > 0);
 
   grpc_tcp_server_start(&exec_ctx, s, NULL, 0, on_connect, NULL);
 
@@ -188,8 +197,10 @@
   while (g_nconnects == nconnects_before &&
          gpr_time_cmp(deadline, gpr_now(deadline.clock_type)) > 0) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(exec_ctx, g_pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC), deadline);
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(exec_ctx, g_pollset, &worker,
+                          gpr_now(GPR_CLOCK_MONOTONIC), deadline)));
     gpr_mu_unlock(g_mu);
     grpc_exec_ctx_finish(exec_ctx);
     gpr_mu_lock(g_mu);
@@ -213,7 +224,8 @@
   int svr_port;
   unsigned svr1_fd_count;
   int svr1_port;
-  grpc_tcp_server *s = grpc_tcp_server_create(NULL);
+  grpc_tcp_server *s;
+  GPR_ASSERT(GRPC_ERROR_NONE == grpc_tcp_server_create(NULL, &s));
   unsigned i;
   server_weak_ref weak_ref;
   server_weak_ref_init(&weak_ref);
@@ -222,14 +234,17 @@
   memset(&addr, 0, sizeof(addr));
   memset(&addr1, 0, sizeof(addr1));
   addr.ss_family = addr1.ss_family = AF_INET;
-  svr_port = grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, addr_len);
+  GPR_ASSERT(GRPC_ERROR_NONE ==
+             grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, addr_len,
+                                      &svr_port));
   GPR_ASSERT(svr_port > 0);
   /* Cannot use wildcard (port==0), because add_port() will try to reuse the
      same port as a previous add_port(). */
   svr1_port = grpc_pick_unused_port_or_die();
   grpc_sockaddr_set_port((struct sockaddr *)&addr1, svr1_port);
-  GPR_ASSERT(grpc_tcp_server_add_port(s, (struct sockaddr *)&addr1, addr_len) ==
-             svr1_port);
+  GPR_ASSERT(grpc_tcp_server_add_port(s, (struct sockaddr *)&addr1, addr_len,
+                                      &svr_port) == GRPC_ERROR_NONE &&
+             svr_port == svr1_port);
 
   /* Bad port_index. */
   GPR_ASSERT(grpc_tcp_server_port_fd_count(s, 2) == 0);
@@ -305,7 +320,8 @@
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
   grpc_pollset_destroy(p);
 }
 
diff --git a/test/core/iomgr/timer_list_test.c b/test/core/iomgr/timer_list_test.c
index 2e0f5c8..be8988a 100644
--- a/test/core/iomgr/timer_list_test.c
+++ b/test/core/iomgr/timer_list_test.c
@@ -42,8 +42,8 @@
 
 static int cb_called[MAX_CB][2];
 
-static void cb(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
-  cb_called[(intptr_t)arg][success]++;
+static void cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  cb_called[(intptr_t)arg][error == GRPC_ERROR_NONE]++;
 }
 
 static void add_test(void) {
diff --git a/test/core/iomgr/workqueue_test.c b/test/core/iomgr/workqueue_test.c
index 953cc35..9a359cd 100644
--- a/test/core/iomgr/workqueue_test.c
+++ b/test/core/iomgr/workqueue_test.c
@@ -42,17 +42,20 @@
 static gpr_mu *g_mu;
 static grpc_pollset *g_pollset;
 
-static void must_succeed(grpc_exec_ctx *exec_ctx, void *p, bool success) {
-  GPR_ASSERT(success == 1);
+static void must_succeed(grpc_exec_ctx *exec_ctx, void *p, grpc_error *error) {
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   gpr_mu_lock(g_mu);
   *(int *)p = 1;
-  grpc_pollset_kick(g_pollset, NULL);
+  GPR_ASSERT(
+      GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(g_pollset, NULL)));
   gpr_mu_unlock(g_mu);
 }
 
 static void test_ref_unref(void) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_workqueue *wq = grpc_workqueue_create(&exec_ctx);
+  grpc_workqueue *wq;
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("grpc_workqueue_create",
+                               grpc_workqueue_create(&exec_ctx, &wq)));
   GRPC_WORKQUEUE_REF(wq, "test");
   GRPC_WORKQUEUE_UNREF(&exec_ctx, wq, "test");
   GRPC_WORKQUEUE_UNREF(&exec_ctx, wq, "destroy");
@@ -63,19 +66,23 @@
   grpc_closure c;
   int done = 0;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_workqueue *wq = grpc_workqueue_create(&exec_ctx);
+  grpc_workqueue *wq;
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("grpc_workqueue_create",
+                               grpc_workqueue_create(&exec_ctx, &wq)));
   gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5);
   grpc_pollset_worker *worker = NULL;
   grpc_closure_init(&c, must_succeed, &done);
 
-  grpc_workqueue_push(wq, &c, 1);
+  grpc_workqueue_push(&exec_ctx, wq, &c, GRPC_ERROR_NONE);
   grpc_workqueue_add_to_pollset(&exec_ctx, wq, g_pollset);
 
   gpr_mu_lock(g_mu);
   GPR_ASSERT(!done);
   while (!done) {
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(deadline.clock_type), deadline);
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(deadline.clock_type), deadline)));
   }
   gpr_mu_unlock(g_mu);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -89,19 +96,24 @@
   grpc_closure c;
   int done = 0;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
-  grpc_workqueue *wq = grpc_workqueue_create(&exec_ctx);
+  grpc_workqueue *wq;
+  GPR_ASSERT(GRPC_LOG_IF_ERROR("grpc_workqueue_create",
+                               grpc_workqueue_create(&exec_ctx, &wq)));
   gpr_timespec deadline = GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5);
   grpc_pollset_worker *worker = NULL;
   grpc_closure_init(&c, must_succeed, &done);
 
-  grpc_exec_ctx_enqueue(&exec_ctx, &c, true, NULL);
+  grpc_exec_ctx_push(&exec_ctx, &c, GRPC_ERROR_NONE, NULL);
   grpc_workqueue_flush(&exec_ctx, wq);
   grpc_workqueue_add_to_pollset(&exec_ctx, wq, g_pollset);
 
   gpr_mu_lock(g_mu);
+  GPR_ASSERT(!done);
   while (!done) {
-    grpc_pollset_work(&exec_ctx, g_pollset, &worker,
-                      gpr_now(deadline.clock_type), deadline);
+    GPR_ASSERT(GRPC_LOG_IF_ERROR(
+        "pollset_work",
+        grpc_pollset_work(&exec_ctx, g_pollset, &worker,
+                          gpr_now(deadline.clock_type), deadline)));
   }
   gpr_mu_unlock(g_mu);
   grpc_exec_ctx_finish(&exec_ctx);
@@ -111,7 +123,8 @@
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
   grpc_pollset_destroy(p);
 }
 
diff --git a/test/core/security/auth_context_test.c b/test/core/security/auth_context_test.c
index d1ead16..e2f44eb 100644
--- a/test/core/security/auth_context_test.c
+++ b/test/core/security/auth_context_test.c
@@ -33,7 +33,7 @@
 
 #include <string.h>
 
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
 #include "src/core/lib/support/string.h"
 #include "test/core/util/test_config.h"
 
diff --git a/test/core/security/b64_test.c b/test/core/security/b64_test.c
index cea8703..b26bd02 100644
--- a/test/core/security/b64_test.c
+++ b/test/core/security/b64_test.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/lib/security/b64.h"
+#include "src/core/lib/security/util/b64.h"
 
 #include <string.h>
 
diff --git a/test/core/security/create_jwt.c b/test/core/security/create_jwt.c
index 6d4707f..3c36b76 100644
--- a/test/core/security/create_jwt.c
+++ b/test/core/security/create_jwt.c
@@ -34,8 +34,7 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "src/core/lib/security/credentials.h"
-#include "src/core/lib/security/json_token.h"
+#include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
 #include "src/core/lib/support/load_file.h"
 
 #include <grpc/support/alloc.h>
diff --git a/test/core/security/credentials_test.c b/test/core/security/credentials_test.c
index 7867293..f8e9720 100644
--- a/test/core/security/credentials_test.c
+++ b/test/core/security/credentials_test.c
@@ -33,7 +33,7 @@
 
 #include <grpc/support/port_platform.h>
 
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 
 #include <openssl/rsa.h>
 #include <stdlib.h>
@@ -45,7 +45,10 @@
 #include <grpc/support/time.h>
 
 #include "src/core/lib/http/httpcli.h"
-#include "src/core/lib/security/json_token.h"
+#include "src/core/lib/security/credentials/composite/composite_credentials.h"
+#include "src/core/lib/security/credentials/google_default/google_default_credentials.h"
+#include "src/core/lib/security/credentials/jwt/jwt_credentials.h"
+#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/support/tmpfile.h"
@@ -546,37 +549,37 @@
 
 static int compute_engine_httpcli_get_success_override(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data) {
-  grpc_httpcli_response response =
-      http_response(200, valid_oauth2_json_response);
+    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_httpcli_response *response) {
   validate_compute_engine_http_request(request);
-  on_response(exec_ctx, user_data, &response);
+  *response = http_response(200, valid_oauth2_json_response);
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
 static int compute_engine_httpcli_get_failure_override(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data) {
-  grpc_httpcli_response response = http_response(403, "Not Authorized.");
+    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_httpcli_response *response) {
   validate_compute_engine_http_request(request);
-  on_response(exec_ctx, user_data, &response);
+  *response = http_response(403, "Not Authorized.");
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
 static int httpcli_post_should_not_be_called(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
     const char *body_bytes, size_t body_size, gpr_timespec deadline,
-    grpc_httpcli_response_cb on_response, void *user_data) {
+    grpc_closure *on_done, grpc_httpcli_response *response) {
   GPR_ASSERT("HTTP POST should not be called" == NULL);
   return 1;
 }
 
-static int httpcli_get_should_not_be_called(
-    grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data) {
+static int httpcli_get_should_not_be_called(grpc_exec_ctx *exec_ctx,
+                                            const grpc_httpcli_request *request,
+                                            gpr_timespec deadline,
+                                            grpc_closure *on_done,
+                                            grpc_httpcli_response *response) {
   GPR_ASSERT("HTTP GET should not be called" == NULL);
   return 1;
 }
@@ -650,21 +653,20 @@
 static int refresh_token_httpcli_post_success(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
     const char *body, size_t body_size, gpr_timespec deadline,
-    grpc_httpcli_response_cb on_response, void *user_data) {
-  grpc_httpcli_response response =
-      http_response(200, valid_oauth2_json_response);
+    grpc_closure *on_done, grpc_httpcli_response *response) {
   validate_refresh_token_http_request(request, body, body_size);
-  on_response(exec_ctx, user_data, &response);
+  *response = http_response(200, valid_oauth2_json_response);
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
 static int refresh_token_httpcli_post_failure(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
     const char *body, size_t body_size, gpr_timespec deadline,
-    grpc_httpcli_response_cb on_response, void *user_data) {
-  grpc_httpcli_response response = http_response(403, "Not Authorized.");
+    grpc_closure *on_done, grpc_httpcli_response *response) {
   validate_refresh_token_http_request(request, body, body_size);
-  on_response(exec_ctx, user_data, &response);
+  *response = http_response(403, "Not Authorized.");
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
@@ -896,17 +898,17 @@
 
 static int default_creds_gce_detection_httpcli_get_success_override(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data) {
-  grpc_httpcli_response response = http_response(200, "");
-  grpc_http_header header;
-  header.key = "Metadata-Flavor";
-  header.value = "Google";
-  response.hdr_count = 1;
-  response.hdrs = &header;
+    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_httpcli_response *response) {
+  *response = http_response(200, "");
+  grpc_http_header *headers = gpr_malloc(sizeof(*headers) * 1);
+  headers[0].key = "Metadata-Flavor";
+  headers[0].value = "Google";
+  response->hdr_count = 1;
+  response->hdrs = headers;
   GPR_ASSERT(strcmp(request->http.path, "/") == 0);
   GPR_ASSERT(strcmp(request->host, "metadata.google.internal") == 0);
-  on_response(exec_ctx, user_data, &response);
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
@@ -958,13 +960,13 @@
 
 static int default_creds_gce_detection_httpcli_get_failure_override(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data) {
+    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_httpcli_response *response) {
   /* No magic header. */
-  grpc_httpcli_response response = http_response(200, "");
   GPR_ASSERT(strcmp(request->http.path, "/") == 0);
   GPR_ASSERT(strcmp(request->host, "metadata.google.internal") == 0);
-  on_response(exec_ctx, user_data, &response);
+  *response = http_response(200, "");
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
diff --git a/test/core/security/fetch_oauth2.c b/test/core/security/fetch_oauth2.c
index bd314e9..2a102fb 100644
--- a/test/core/security/fetch_oauth2.c
+++ b/test/core/security/fetch_oauth2.c
@@ -42,7 +42,7 @@
 #include <grpc/support/slice.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/support/load_file.h"
 #include "test/core/security/oauth2_utils.h"
 
diff --git a/test/core/security/json_token_test.c b/test/core/security/json_token_test.c
index 3aee52e..405fe56 100644
--- a/test/core/security/json_token_test.c
+++ b/test/core/security/json_token_test.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/lib/security/json_token.h"
+#include "src/core/lib/security/credentials/jwt/json_token.h"
 
 #include <openssl/evp.h>
 #include <string.h>
@@ -42,7 +42,8 @@
 #include <grpc/support/slice.h>
 
 #include "src/core/lib/json/json.h"
-#include "src/core/lib/security/b64.h"
+#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h"
+#include "src/core/lib/security/util/b64.h"
 #include "test/core/util/test_config.h"
 
 /* This JSON key was generated with the GCE console and revoked immediately.
diff --git a/test/core/security/jwt_verifier_test.c b/test/core/security/jwt_verifier_test.c
index 077f44d..23b4695 100644
--- a/test/core/security/jwt_verifier_test.c
+++ b/test/core/security/jwt_verifier_test.c
@@ -31,7 +31,7 @@
  *
  */
 
-#include "src/core/lib/security/jwt_verifier.h"
+#include "src/core/lib/security/credentials/jwt/jwt_verifier.h"
 
 #include <string.h>
 
@@ -43,8 +43,8 @@
 #include <grpc/support/string_util.h>
 
 #include "src/core/lib/http/httpcli.h"
-#include "src/core/lib/security/b64.h"
-#include "src/core/lib/security/json_token.h"
+#include "src/core/lib/security/credentials/jwt/json_token.h"
+#include "src/core/lib/security/util/b64.h"
 #include "test/core/util/test_config.h"
 
 /* This JSON key was generated with the GCE console and revoked immediately.
@@ -278,24 +278,23 @@
 static int httpcli_post_should_not_be_called(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
     const char *body_bytes, size_t body_size, gpr_timespec deadline,
-    grpc_httpcli_response_cb on_response, void *user_data) {
+    grpc_closure *on_done, grpc_httpcli_response *response) {
   GPR_ASSERT("HTTP POST should not be called" == NULL);
   return 1;
 }
 
 static int httpcli_get_google_keys_for_email(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data) {
-  grpc_httpcli_response response = http_response(200, good_google_email_keys());
+    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_httpcli_response *response) {
+  *response = http_response(200, good_google_email_keys());
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
   GPR_ASSERT(strcmp(request->host, "www.googleapis.com") == 0);
   GPR_ASSERT(strcmp(request->http.path,
                     "/robot/v1/metadata/x509/"
                     "777-abaslkan11hlb6nmim3bpspl31ud@developer."
                     "gserviceaccount.com") == 0);
-  on_response(exec_ctx, user_data, &response);
-  gpr_free(response.body);
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
@@ -325,22 +324,21 @@
   GPR_ASSERT(jwt != NULL);
   grpc_jwt_verifier_verify(&exec_ctx, verifier, NULL, jwt, expected_audience,
                            on_verification_success, (void *)expected_user_data);
+  grpc_exec_ctx_finish(&exec_ctx);
   gpr_free(jwt);
   grpc_jwt_verifier_destroy(verifier);
   grpc_httpcli_set_override(NULL, NULL);
-  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static int httpcli_get_custom_keys_for_email(
     grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data) {
-  grpc_httpcli_response response = http_response(200, gpr_strdup(good_jwk_set));
+    gpr_timespec deadline, grpc_closure *on_done,
+    grpc_httpcli_response *response) {
+  *response = http_response(200, gpr_strdup(good_jwk_set));
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
   GPR_ASSERT(strcmp(request->host, "keys.bar.com") == 0);
   GPR_ASSERT(strcmp(request->http.path, "/jwk/foo@bar.com") == 0);
-  on_response(exec_ctx, user_data, &response);
-  gpr_free(response.body);
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
@@ -360,40 +358,36 @@
   GPR_ASSERT(jwt != NULL);
   grpc_jwt_verifier_verify(&exec_ctx, verifier, NULL, jwt, expected_audience,
                            on_verification_success, (void *)expected_user_data);
+  grpc_exec_ctx_finish(&exec_ctx);
   gpr_free(jwt);
   grpc_jwt_verifier_destroy(verifier);
   grpc_httpcli_set_override(NULL, NULL);
-  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static int httpcli_get_jwk_set(grpc_exec_ctx *exec_ctx,
                                const grpc_httpcli_request *request,
-                               gpr_timespec deadline,
-                               grpc_httpcli_response_cb on_response,
-                               void *user_data) {
-  grpc_httpcli_response response = http_response(200, gpr_strdup(good_jwk_set));
+                               gpr_timespec deadline, grpc_closure *on_done,
+                               grpc_httpcli_response *response) {
+  *response = http_response(200, gpr_strdup(good_jwk_set));
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
   GPR_ASSERT(strcmp(request->host, "www.googleapis.com") == 0);
   GPR_ASSERT(strcmp(request->http.path, "/oauth2/v3/certs") == 0);
-  on_response(exec_ctx, user_data, &response);
-  gpr_free(response.body);
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
 static int httpcli_get_openid_config(grpc_exec_ctx *exec_ctx,
                                      const grpc_httpcli_request *request,
                                      gpr_timespec deadline,
-                                     grpc_httpcli_response_cb on_response,
-                                     void *user_data) {
-  grpc_httpcli_response response =
-      http_response(200, gpr_strdup(good_openid_config));
+                                     grpc_closure *on_done,
+                                     grpc_httpcli_response *response) {
+  *response = http_response(200, gpr_strdup(good_openid_config));
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
   GPR_ASSERT(strcmp(request->host, "accounts.google.com") == 0);
   GPR_ASSERT(strcmp(request->http.path, GRPC_OPENID_CONFIG_URL_SUFFIX) == 0);
   grpc_httpcli_set_override(httpcli_get_jwk_set,
                             httpcli_post_should_not_be_called);
-  on_response(exec_ctx, user_data, &response);
-  gpr_free(response.body);
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
@@ -413,10 +407,10 @@
   GPR_ASSERT(jwt != NULL);
   grpc_jwt_verifier_verify(&exec_ctx, verifier, NULL, jwt, expected_audience,
                            on_verification_success, (void *)expected_user_data);
+  grpc_exec_ctx_finish(&exec_ctx);
   gpr_free(jwt);
   grpc_jwt_verifier_destroy(verifier);
   grpc_httpcli_set_override(NULL, NULL);
-  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void on_verification_key_retrieval_error(void *user_data,
@@ -429,14 +423,11 @@
 
 static int httpcli_get_bad_json(grpc_exec_ctx *exec_ctx,
                                 const grpc_httpcli_request *request,
-                                gpr_timespec deadline,
-                                grpc_httpcli_response_cb on_response,
-                                void *user_data) {
-  grpc_httpcli_response response =
-      http_response(200, gpr_strdup("{\"bad\": \"stuff\"}"));
+                                gpr_timespec deadline, grpc_closure *on_done,
+                                grpc_httpcli_response *response) {
+  *response = http_response(200, gpr_strdup("{\"bad\": \"stuff\"}"));
   GPR_ASSERT(request->handshaker == &grpc_httpcli_ssl);
-  on_response(exec_ctx, user_data, &response);
-  gpr_free(response.body);
+  grpc_exec_ctx_push(exec_ctx, on_done, GRPC_ERROR_NONE, NULL);
   return 1;
 }
 
@@ -457,10 +448,10 @@
   grpc_jwt_verifier_verify(&exec_ctx, verifier, NULL, jwt, expected_audience,
                            on_verification_key_retrieval_error,
                            (void *)expected_user_data);
+  grpc_exec_ctx_finish(&exec_ctx);
   gpr_free(jwt);
   grpc_jwt_verifier_destroy(verifier);
   grpc_httpcli_set_override(NULL, NULL);
-  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void test_jwt_verifier_bad_json_key(void) {
@@ -480,10 +471,10 @@
   grpc_jwt_verifier_verify(&exec_ctx, verifier, NULL, jwt, expected_audience,
                            on_verification_key_retrieval_error,
                            (void *)expected_user_data);
+  grpc_exec_ctx_finish(&exec_ctx);
   gpr_free(jwt);
   grpc_jwt_verifier_destroy(verifier);
   grpc_httpcli_set_override(NULL, NULL);
-  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 static void corrupt_jwt_sig(char *jwt) {
@@ -529,16 +520,17 @@
   grpc_jwt_verifier_verify(&exec_ctx, verifier, NULL, jwt, expected_audience,
                            on_verification_bad_signature,
                            (void *)expected_user_data);
+  grpc_exec_ctx_finish(&exec_ctx);
   gpr_free(jwt);
   grpc_jwt_verifier_destroy(verifier);
   grpc_httpcli_set_override(NULL, NULL);
-  grpc_exec_ctx_finish(&exec_ctx);
 }
 
-static int httpcli_get_should_not_be_called(
-    grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request,
-    gpr_timespec deadline, grpc_httpcli_response_cb on_response,
-    void *user_data) {
+static int httpcli_get_should_not_be_called(grpc_exec_ctx *exec_ctx,
+                                            const grpc_httpcli_request *request,
+                                            gpr_timespec deadline,
+                                            grpc_closure *on_done,
+                                            grpc_httpcli_response *response) {
   GPR_ASSERT(0);
   return 1;
 }
@@ -559,9 +551,9 @@
   grpc_jwt_verifier_verify(&exec_ctx, verifier, NULL, "bad jwt",
                            expected_audience, on_verification_bad_format,
                            (void *)expected_user_data);
+  grpc_exec_ctx_finish(&exec_ctx);
   grpc_jwt_verifier_destroy(verifier);
   grpc_httpcli_set_override(NULL, NULL);
-  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /* find verification key: bad jks, cannot find key in jks */
diff --git a/test/core/security/oauth2_utils.c b/test/core/security/oauth2_utils.c
index 20815d1..c92850d 100644
--- a/test/core/security/oauth2_utils.c
+++ b/test/core/security/oauth2_utils.c
@@ -42,7 +42,7 @@
 #include <grpc/support/slice.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 
 typedef struct {
   gpr_mu *mu;
@@ -70,11 +70,12 @@
   gpr_mu_lock(request->mu);
   request->is_done = 1;
   request->token = token;
-  grpc_pollset_kick(request->pollset, NULL);
+  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(request->pollset, NULL));
   gpr_mu_unlock(request->mu);
 }
 
-static void do_nothing(grpc_exec_ctx *exec_ctx, void *unused, bool success) {}
+static void do_nothing(grpc_exec_ctx *exec_ctx, void *unused,
+                       grpc_error *error) {}
 
 char *grpc_test_fetch_oauth2_token_with_credentials(
     grpc_call_credentials *creds) {
@@ -98,9 +99,13 @@
   gpr_mu_lock(request.mu);
   while (!request.is_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, request.pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    if (GRPC_LOG_IF_ERROR(
+            "pollset_work",
+            grpc_pollset_work(&exec_ctx, request.pollset, &worker,
+                              gpr_now(GPR_CLOCK_MONOTONIC),
+                              gpr_inf_future(GPR_CLOCK_MONOTONIC)))) {
+      request.is_done = 1;
+    }
   }
   gpr_mu_unlock(request.mu);
 
diff --git a/test/core/security/oauth2_utils.h b/test/core/security/oauth2_utils.h
index eff9827..0f4e885 100644
--- a/test/core/security/oauth2_utils.h
+++ b/test/core/security/oauth2_utils.h
@@ -34,7 +34,7 @@
 #ifndef GRPC_TEST_CORE_SECURITY_OAUTH2_UTILS_H
 #define GRPC_TEST_CORE_SECURITY_OAUTH2_UTILS_H
 
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/test/core/security/print_google_default_creds_token.c b/test/core/security/print_google_default_creds_token.c
index 99bce4f..33e59c2 100644
--- a/test/core/security/print_google_default_creds_token.c
+++ b/test/core/security/print_google_default_creds_token.c
@@ -42,7 +42,8 @@
 #include <grpc/support/slice.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/composite/composite_credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 #include "src/core/lib/support/string.h"
 
 typedef struct {
@@ -66,7 +67,7 @@
   }
   gpr_mu_lock(sync->mu);
   sync->is_done = 1;
-  grpc_pollset_kick(sync->pollset, NULL);
+  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(sync->pollset, NULL));
   gpr_mu_unlock(sync->mu);
 }
 
@@ -104,9 +105,12 @@
   gpr_mu_lock(sync.mu);
   while (!sync.is_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, sync.pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    if (!GRPC_LOG_IF_ERROR(
+            "pollset_work",
+            grpc_pollset_work(&exec_ctx, sync.pollset, &worker,
+                              gpr_now(GPR_CLOCK_MONOTONIC),
+                              gpr_inf_future(GPR_CLOCK_MONOTONIC))))
+      sync.is_done = 1;
     gpr_mu_unlock(sync.mu);
     grpc_exec_ctx_flush(&exec_ctx);
     gpr_mu_lock(sync.mu);
diff --git a/test/core/security/secure_endpoint_test.c b/test/core/security/secure_endpoint_test.c
index aeaf382..1d2bf73 100644
--- a/test/core/security/secure_endpoint_test.c
+++ b/test/core/security/secure_endpoint_test.c
@@ -41,7 +41,7 @@
 #include <grpc/support/log.h>
 #include "src/core/lib/iomgr/endpoint_pair.h"
 #include "src/core/lib/iomgr/iomgr.h"
-#include "src/core/lib/security/secure_endpoint.h"
+#include "src/core/lib/security/transport/secure_endpoint.h"
 #include "src/core/lib/tsi/fake_transport_security.h"
 #include "test/core/util/test_config.h"
 
@@ -139,7 +139,8 @@
      secure_endpoint_create_fixture_tcp_socketpair_leftover, clean_up},
 };
 
-static void inc_call_ctr(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+static void inc_call_ctr(grpc_exec_ctx *exec_ctx, void *arg,
+                         grpc_error *error) {
   ++*(int *)arg;
 }
 
@@ -172,7 +173,8 @@
   clean_up();
 }
 
-static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, bool success) {
+static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p,
+                            grpc_error *error) {
   grpc_pollset_destroy(p);
 }
 
diff --git a/test/core/security/security_connector_test.c b/test/core/security/security_connector_test.c
index 1a4e64b..6106bec 100644
--- a/test/core/security/security_connector_test.c
+++ b/test/core/security/security_connector_test.c
@@ -40,8 +40,8 @@
 #include <grpc/support/string_util.h>
 #include <grpc/support/useful.h>
 
-#include "src/core/lib/security/security_connector.h"
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
+#include "src/core/lib/security/transport/security_connector.h"
 #include "src/core/lib/support/env.h"
 #include "src/core/lib/support/string.h"
 #include "src/core/lib/support/tmpfile.h"
diff --git a/test/core/security/verify_jwt.c b/test/core/security/verify_jwt.c
index 2274fe1..728d2d6 100644
--- a/test/core/security/verify_jwt.c
+++ b/test/core/security/verify_jwt.c
@@ -42,7 +42,7 @@
 #include <grpc/support/slice.h>
 #include <grpc/support/sync.h>
 
-#include "src/core/lib/security/jwt_verifier.h"
+#include "src/core/lib/security/credentials/jwt/jwt_verifier.h"
 
 typedef struct {
   grpc_pollset *pollset;
@@ -81,7 +81,7 @@
 
   gpr_mu_lock(sync->mu);
   sync->is_done = 1;
-  grpc_pollset_kick(sync->pollset, NULL);
+  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(sync->pollset, NULL));
   gpr_mu_unlock(sync->mu);
 }
 
@@ -115,9 +115,12 @@
   gpr_mu_lock(sync.mu);
   while (!sync.is_done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, sync.pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      gpr_inf_future(GPR_CLOCK_MONOTONIC));
+    if (!GRPC_LOG_IF_ERROR(
+            "pollset_work",
+            grpc_pollset_work(&exec_ctx, sync.pollset, &worker,
+                              gpr_now(GPR_CLOCK_MONOTONIC),
+                              gpr_inf_future(GPR_CLOCK_MONOTONIC))))
+      sync.is_done = true;
     gpr_mu_unlock(sync.mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(sync.mu);
diff --git a/test/core/support/load_file_test.c b/test/core/support/load_file_test.c
index 0125fd9..9874db3 100644
--- a/test/core/support/load_file_test.c
+++ b/test/core/support/load_file_test.c
@@ -51,7 +51,7 @@
   FILE *tmp = NULL;
   gpr_slice slice;
   gpr_slice slice_with_null_term;
-  int success;
+  grpc_error *error;
   char *tmp_name;
 
   LOG_TEST_NAME("test_load_empty_file");
@@ -61,12 +61,12 @@
   GPR_ASSERT(tmp != NULL);
   fclose(tmp);
 
-  slice = gpr_load_file(tmp_name, 0, &success);
-  GPR_ASSERT(success == 1);
+  error = gpr_load_file(tmp_name, 0, &slice);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   GPR_ASSERT(GPR_SLICE_LENGTH(slice) == 0);
 
-  slice_with_null_term = gpr_load_file(tmp_name, 1, &success);
-  GPR_ASSERT(success == 1);
+  error = gpr_load_file(tmp_name, 1, &slice_with_null_term);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   GPR_ASSERT(GPR_SLICE_LENGTH(slice_with_null_term) == 1);
   GPR_ASSERT(GPR_SLICE_START_PTR(slice_with_null_term)[0] == 0);
 
@@ -79,7 +79,7 @@
 static void test_load_failure(void) {
   FILE *tmp = NULL;
   gpr_slice slice;
-  int success;
+  grpc_error *error;
   char *tmp_name;
 
   LOG_TEST_NAME("test_load_failure");
@@ -90,8 +90,9 @@
   fclose(tmp);
   remove(tmp_name);
 
-  slice = gpr_load_file(tmp_name, 0, &success);
-  GPR_ASSERT(success == 0);
+  error = gpr_load_file(tmp_name, 0, &slice);
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  GRPC_ERROR_UNREF(error);
   GPR_ASSERT(GPR_SLICE_LENGTH(slice) == 0);
   gpr_free(tmp_name);
   gpr_slice_unref(slice);
@@ -101,7 +102,7 @@
   FILE *tmp = NULL;
   gpr_slice slice;
   gpr_slice slice_with_null_term;
-  int success;
+  grpc_error *error;
   char *tmp_name;
   const char *blah = "blah";
 
@@ -113,13 +114,13 @@
   GPR_ASSERT(fwrite(blah, 1, strlen(blah), tmp) == strlen(blah));
   fclose(tmp);
 
-  slice = gpr_load_file(tmp_name, 0, &success);
-  GPR_ASSERT(success == 1);
+  error = gpr_load_file(tmp_name, 0, &slice);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   GPR_ASSERT(GPR_SLICE_LENGTH(slice) == strlen(blah));
   GPR_ASSERT(!memcmp(GPR_SLICE_START_PTR(slice), blah, strlen(blah)));
 
-  slice_with_null_term = gpr_load_file(tmp_name, 1, &success);
-  GPR_ASSERT(success == 1);
+  error = gpr_load_file(tmp_name, 1, &slice_with_null_term);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   GPR_ASSERT(GPR_SLICE_LENGTH(slice_with_null_term) == (strlen(blah) + 1));
   GPR_ASSERT(strcmp((const char *)GPR_SLICE_START_PTR(slice_with_null_term),
                     blah) == 0);
@@ -133,7 +134,7 @@
 static void test_load_big_file(void) {
   FILE *tmp = NULL;
   gpr_slice slice;
-  int success;
+  grpc_error *error;
   char *tmp_name;
   static const size_t buffer_size = 124631;
   unsigned char *buffer = gpr_malloc(buffer_size);
@@ -150,8 +151,8 @@
   GPR_ASSERT(fwrite(buffer, 1, buffer_size, tmp) == buffer_size);
   fclose(tmp);
 
-  slice = gpr_load_file(tmp_name, 0, &success);
-  GPR_ASSERT(success == 1);
+  error = gpr_load_file(tmp_name, 0, &slice);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   GPR_ASSERT(GPR_SLICE_LENGTH(slice) == buffer_size);
   current = GPR_SLICE_START_PTR(slice);
   for (i = 0; i < buffer_size; i++) {
diff --git a/test/core/surface/completion_queue_test.c b/test/core/surface/completion_queue_test.c
index d62d5a9..1008778 100644
--- a/test/core/surface/completion_queue_test.c
+++ b/test/core/surface/completion_queue_test.c
@@ -90,8 +90,8 @@
   cc = grpc_completion_queue_create(NULL);
 
   grpc_cq_begin_op(cc, tag);
-  grpc_cq_end_op(&exec_ctx, cc, tag, 1, do_nothing_end_completion, NULL,
-                 &completion);
+  grpc_cq_end_op(&exec_ctx, cc, tag, GRPC_ERROR_NONE, do_nothing_end_completion,
+                 NULL, &completion);
 
   ev = grpc_completion_queue_next(cc, gpr_inf_past(GPR_CLOCK_REALTIME), NULL);
   GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
@@ -149,8 +149,8 @@
 
   for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
     grpc_cq_begin_op(cc, tags[i]);
-    grpc_cq_end_op(&exec_ctx, cc, tags[i], 1, do_nothing_end_completion, NULL,
-                   &completions[i]);
+    grpc_cq_end_op(&exec_ctx, cc, tags[i], GRPC_ERROR_NONE,
+                   do_nothing_end_completion, NULL, &completions[i]);
   }
 
   for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
@@ -161,8 +161,8 @@
 
   for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
     grpc_cq_begin_op(cc, tags[i]);
-    grpc_cq_end_op(&exec_ctx, cc, tags[i], 1, do_nothing_end_completion, NULL,
-                   &completions[i]);
+    grpc_cq_end_op(&exec_ctx, cc, tags[i], GRPC_ERROR_NONE,
+                   do_nothing_end_completion, NULL, &completions[i]);
   }
 
   for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
@@ -234,8 +234,8 @@
 
   for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
     grpc_cq_begin_op(cc, tags[i]);
-    grpc_cq_end_op(&exec_ctx, cc, tags[i], 1, do_nothing_end_completion, NULL,
-                   &completions[i]);
+    grpc_cq_end_op(&exec_ctx, cc, tags[i], GRPC_ERROR_NONE,
+                   do_nothing_end_completion, NULL, &completions[i]);
   }
 
   for (i = 0; i < GPR_ARRAY_SIZE(tags); i++) {
@@ -288,8 +288,9 @@
 
   gpr_log(GPR_INFO, "producer %d phase 2", opt->id);
   for (i = 0; i < TEST_THREAD_EVENTS; i++) {
-    grpc_cq_end_op(&exec_ctx, opt->cc, (void *)(intptr_t)1, 1, free_completion,
-                   NULL, gpr_malloc(sizeof(grpc_cq_completion)));
+    grpc_cq_end_op(&exec_ctx, opt->cc, (void *)(intptr_t)1, GRPC_ERROR_NONE,
+                   free_completion, NULL,
+                   gpr_malloc(sizeof(grpc_cq_completion)));
     opt->events_triggered++;
     grpc_exec_ctx_finish(&exec_ctx);
   }
diff --git a/test/core/surface/concurrent_connectivity_test.c b/test/core/surface/concurrent_connectivity_test.c
index 28ddf58..917c475 100644
--- a/test/core/surface/concurrent_connectivity_test.c
+++ b/test/core/surface/concurrent_connectivity_test.c
@@ -101,7 +101,7 @@
   (void)acceptor;
   grpc_endpoint_shutdown(exec_ctx, tcp);
   grpc_endpoint_destroy(exec_ctx, tcp);
-  grpc_pollset_kick(args->pollset, NULL);
+  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(args->pollset, NULL));
 }
 
 void bad_server_thread(void *vargs) {
@@ -111,10 +111,13 @@
   struct sockaddr_storage addr;
   socklen_t addr_len = sizeof(addr);
   int port;
-  grpc_tcp_server *s = grpc_tcp_server_create(NULL);
+  grpc_tcp_server *s;
+  grpc_error *error = grpc_tcp_server_create(NULL, &s);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   memset(&addr, 0, sizeof(addr));
   addr.ss_family = AF_INET;
-  port = grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, addr_len);
+  error =
+      grpc_tcp_server_add_port(s, (struct sockaddr *)&addr, addr_len, &port);
   GPR_ASSERT(port > 0);
   gpr_asprintf(&args->addr, "localhost:%d", port);
 
@@ -128,7 +131,11 @@
         gpr_time_add(now, gpr_time_from_millis(100, GPR_TIMESPAN));
 
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, args->pollset, &worker, now, deadline);
+    if (!GRPC_LOG_IF_ERROR("pollset_work",
+                           grpc_pollset_work(&exec_ctx, args->pollset, &worker,
+                                             now, deadline))) {
+      gpr_atm_rel_store(&args->stop, 1);
+    }
     gpr_mu_unlock(args->mu);
     grpc_exec_ctx_finish(&exec_ctx);
     gpr_mu_lock(args->mu);
@@ -143,7 +150,7 @@
 }
 
 static void done_pollset_shutdown(grpc_exec_ctx *exec_ctx, void *pollset,
-                                  bool success) {
+                                  grpc_error *error) {
   grpc_pollset_destroy(pollset);
   gpr_free(pollset);
 }
diff --git a/test/core/surface/lame_client_test.c b/test/core/surface/lame_client_test.c
index 12fa9de..68f75bc 100644
--- a/test/core/surface/lame_client_test.c
+++ b/test/core/surface/lame_client_test.c
@@ -47,13 +47,14 @@
 
 static void *tag(intptr_t x) { return (void *)x; }
 
-void verify_connectivity(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+void verify_connectivity(grpc_exec_ctx *exec_ctx, void *arg,
+                         grpc_error *error) {
   grpc_transport_op *op = arg;
   GPR_ASSERT(GRPC_CHANNEL_FATAL_FAILURE == *op->connectivity_state);
-  GPR_ASSERT(success);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
 }
 
-void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, bool success) {}
+void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {}
 
 void test_transport_op(grpc_channel *channel) {
   grpc_transport_op op;
diff --git a/test/core/surface/secure_channel_create_test.c b/test/core/surface/secure_channel_create_test.c
index 80419ef..b952503 100644
--- a/test/core/surface/secure_channel_create_test.c
+++ b/test/core/surface/secure_channel_create_test.c
@@ -37,8 +37,8 @@
 #include <grpc/grpc_security.h>
 #include <grpc/support/log.h>
 #include "src/core/ext/client_config/resolver_registry.h"
-#include "src/core/lib/security/credentials.h"
-#include "src/core/lib/security/security_connector.h"
+#include "src/core/lib/security/credentials/fake/fake_credentials.h"
+#include "src/core/lib/security/transport/security_connector.h"
 #include "src/core/lib/surface/channel.h"
 #include "test/core/util/test_config.h"
 
diff --git a/test/core/surface/server_chttp2_test.c b/test/core/surface/server_chttp2_test.c
index d22c164..f42ca9f 100644
--- a/test/core/surface/server_chttp2_test.c
+++ b/test/core/surface/server_chttp2_test.c
@@ -37,7 +37,8 @@
 #include <grpc/support/host_port.h>
 #include <grpc/support/log.h>
 #include <grpc/support/time.h>
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
+#include "src/core/lib/security/credentials/fake/fake_credentials.h"
 #include "src/core/lib/tsi/fake_transport_security.h"
 #include "test/core/util/port.h"
 #include "test/core/util/test_config.h"
diff --git a/test/core/transport/chttp2/hpack_parser_fuzzer_test.c b/test/core/transport/chttp2/hpack_parser_fuzzer_test.c
index e41eda8..7c2efc3 100644
--- a/test/core/transport/chttp2/hpack_parser_fuzzer_test.c
+++ b/test/core/transport/chttp2/hpack_parser_fuzzer_test.c
@@ -50,7 +50,7 @@
   grpc_chttp2_hpack_parser parser;
   grpc_chttp2_hpack_parser_init(&parser);
   parser.on_header = onhdr;
-  grpc_chttp2_hpack_parser_parse(&parser, data, data + size);
+  GRPC_ERROR_UNREF(grpc_chttp2_hpack_parser_parse(&parser, data, data + size));
   grpc_chttp2_hpack_parser_destroy(&parser);
   grpc_shutdown();
   return 0;
diff --git a/test/core/transport/chttp2/hpack_parser_test.c b/test/core/transport/chttp2/hpack_parser_test.c
index 51bf48d..9ddceb8 100644
--- a/test/core/transport/chttp2/hpack_parser_test.c
+++ b/test/core/transport/chttp2/hpack_parser_test.c
@@ -76,7 +76,8 @@
 
   for (i = 0; i < nslices; i++) {
     GPR_ASSERT(grpc_chttp2_hpack_parser_parse(
-        parser, GPR_SLICE_START_PTR(slices[i]), GPR_SLICE_END_PTR(slices[i])));
+                   parser, GPR_SLICE_START_PTR(slices[i]),
+                   GPR_SLICE_END_PTR(slices[i])) == GRPC_ERROR_NONE);
   }
 
   for (i = 0; i < nslices; i++) {
diff --git a/test/core/transport/chttp2/hpack_table_test.c b/test/core/transport/chttp2/hpack_table_test.c
index 73e59f1..98e486c 100644
--- a/test/core/transport/chttp2/hpack_table_test.c
+++ b/test/core/transport/chttp2/hpack_table_test.c
@@ -144,7 +144,7 @@
     gpr_asprintf(&key, "K:%d", i);
     gpr_asprintf(&value, "VALUE:%d", i);
     elem = grpc_mdelem_from_strings(key, value);
-    GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
+    GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
     GRPC_MDELEM_UNREF(elem);
     assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
     gpr_free(key);
@@ -181,13 +181,13 @@
 
   grpc_chttp2_hptbl_init(&tbl);
   elem = grpc_mdelem_from_strings("abc", "xyz");
-  GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
+  GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
   GRPC_MDELEM_UNREF(elem);
   elem = grpc_mdelem_from_strings("abc", "123");
-  GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
+  GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
   GRPC_MDELEM_UNREF(elem);
   elem = grpc_mdelem_from_strings("x", "1");
-  GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
+  GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
   GRPC_MDELEM_UNREF(elem);
 
   r = find_simple(&tbl, "abc", "123");
@@ -238,7 +238,7 @@
   for (i = 0; i < 10000; i++) {
     int64_ttoa(i, buffer);
     elem = grpc_mdelem_from_strings("test", buffer);
-    GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem));
+    GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
     GRPC_MDELEM_UNREF(elem);
   }
 
diff --git a/test/core/transport/connectivity_state_test.c b/test/core/transport/connectivity_state_test.c
index 6bb7c3b..0b7a701 100644
--- a/test/core/transport/connectivity_state_test.c
+++ b/test/core/transport/connectivity_state_test.c
@@ -43,14 +43,15 @@
 
 int g_counter;
 
-static void must_succeed(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
-  GPR_ASSERT(success);
+static void must_succeed(grpc_exec_ctx *exec_ctx, void *arg,
+                         grpc_error *error) {
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   GPR_ASSERT(arg == THE_ARG);
   g_counter++;
 }
 
-static void must_fail(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
-  GPR_ASSERT(!success);
+static void must_fail(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
   GPR_ASSERT(arg == THE_ARG);
   g_counter++;
 }
@@ -74,9 +75,12 @@
 static void test_check(void) {
   grpc_connectivity_state_tracker tracker;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+  grpc_error *error;
   gpr_log(GPR_DEBUG, "test_check");
   grpc_connectivity_state_init(&tracker, GRPC_CHANNEL_IDLE, "xxx");
-  GPR_ASSERT(grpc_connectivity_state_check(&tracker) == GRPC_CHANNEL_IDLE);
+  GPR_ASSERT(grpc_connectivity_state_check(&tracker, &error) ==
+             GRPC_CHANNEL_IDLE);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   grpc_connectivity_state_destroy(&exec_ctx, &tracker);
   grpc_exec_ctx_finish(&exec_ctx);
 }
diff --git a/test/core/util/mock_endpoint.c b/test/core/util/mock_endpoint.c
index 7768413..deef68e 100644
--- a/test/core/util/mock_endpoint.c
+++ b/test/core/util/mock_endpoint.c
@@ -51,7 +51,7 @@
   gpr_mu_lock(&m->mu);
   if (m->read_buffer.count > 0) {
     gpr_slice_buffer_swap(&m->read_buffer, slices);
-    grpc_exec_ctx_enqueue(exec_ctx, cb, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, cb, GRPC_ERROR_NONE, NULL);
   } else {
     m->on_read = cb;
     m->on_read_out = slices;
@@ -65,7 +65,7 @@
   for (size_t i = 0; i < slices->count; i++) {
     m->on_write(slices->slices[i]);
   }
-  grpc_exec_ctx_enqueue(exec_ctx, cb, true, NULL);
+  grpc_exec_ctx_push(exec_ctx, cb, GRPC_ERROR_NONE, NULL);
 }
 
 static void me_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
@@ -78,7 +78,8 @@
   grpc_mock_endpoint *m = (grpc_mock_endpoint *)ep;
   gpr_mu_lock(&m->mu);
   if (m->on_read) {
-    grpc_exec_ctx_enqueue(exec_ctx, m->on_read, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, m->on_read,
+                       GRPC_ERROR_CREATE("Endpoint Shutdown"), NULL);
     m->on_read = NULL;
   }
   gpr_mu_unlock(&m->mu);
@@ -115,7 +116,7 @@
   gpr_mu_lock(&m->mu);
   if (m->on_read != NULL) {
     gpr_slice_buffer_add(m->on_read_out, slice);
-    grpc_exec_ctx_enqueue(exec_ctx, m->on_read, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, m->on_read, GRPC_ERROR_NONE, NULL);
     m->on_read = NULL;
   } else {
     gpr_slice_buffer_add(&m->read_buffer, slice);
diff --git a/test/core/util/passthru_endpoint.c b/test/core/util/passthru_endpoint.c
index ae955b1..93753be 100644
--- a/test/core/util/passthru_endpoint.c
+++ b/test/core/util/passthru_endpoint.c
@@ -59,10 +59,11 @@
   half *m = (half *)ep;
   gpr_mu_lock(&m->parent->mu);
   if (m->parent->shutdown) {
-    grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, cb, GRPC_ERROR_CREATE("Already shutdown"),
+                       NULL);
   } else if (m->read_buffer.count > 0) {
     gpr_slice_buffer_swap(&m->read_buffer, slices);
-    grpc_exec_ctx_enqueue(exec_ctx, cb, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, cb, GRPC_ERROR_NONE, NULL);
   } else {
     m->on_read = cb;
     m->on_read_out = slices;
@@ -79,14 +80,14 @@
                      gpr_slice_buffer *slices, grpc_closure *cb) {
   half *m = other_half((half *)ep);
   gpr_mu_lock(&m->parent->mu);
-  bool ok = true;
+  grpc_error *error = GRPC_ERROR_NONE;
   if (m->parent->shutdown) {
-    ok = false;
+    error = GRPC_ERROR_CREATE("Endpoint already shutdown");
   } else if (m->on_read != NULL) {
     for (size_t i = 0; i < slices->count; i++) {
       gpr_slice_buffer_add(m->on_read_out, gpr_slice_ref(slices->slices[i]));
     }
-    grpc_exec_ctx_enqueue(exec_ctx, m->on_read, true, NULL);
+    grpc_exec_ctx_push(exec_ctx, m->on_read, GRPC_ERROR_NONE, NULL);
     m->on_read = NULL;
   } else {
     for (size_t i = 0; i < slices->count; i++) {
@@ -94,7 +95,7 @@
     }
   }
   gpr_mu_unlock(&m->parent->mu);
-  grpc_exec_ctx_enqueue(exec_ctx, cb, ok, NULL);
+  grpc_exec_ctx_push(exec_ctx, cb, error, NULL);
 }
 
 static void me_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep,
@@ -108,12 +109,14 @@
   gpr_mu_lock(&m->parent->mu);
   m->parent->shutdown = true;
   if (m->on_read) {
-    grpc_exec_ctx_enqueue(exec_ctx, m->on_read, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, m->on_read, GRPC_ERROR_CREATE("Shutdown"),
+                       NULL);
     m->on_read = NULL;
   }
   m = other_half(m);
   if (m->on_read) {
-    grpc_exec_ctx_enqueue(exec_ctx, m->on_read, false, NULL);
+    grpc_exec_ctx_push(exec_ctx, m->on_read, GRPC_ERROR_CREATE("Shutdown"),
+                       NULL);
     m->on_read = NULL;
   }
   gpr_mu_unlock(&m->parent->mu);
diff --git a/test/core/util/port_server_client.c b/test/core/util/port_server_client.c
index 84e9054..167efb5 100644
--- a/test/core/util/port_server_client.c
+++ b/test/core/util/port_server_client.c
@@ -56,24 +56,25 @@
 } freereq;
 
 static void destroy_pollset_and_shutdown(grpc_exec_ctx *exec_ctx, void *p,
-                                         bool success) {
+                                         grpc_error *error) {
   grpc_pollset_destroy(p);
   gpr_free(p);
   grpc_shutdown();
 }
 
 static void freed_port_from_server(grpc_exec_ctx *exec_ctx, void *arg,
-                                   const grpc_httpcli_response *response) {
+                                   grpc_error *error) {
   freereq *pr = arg;
   gpr_mu_lock(pr->mu);
   pr->done = 1;
-  grpc_pollset_kick(pr->pollset, NULL);
+  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(pr->pollset, NULL));
   gpr_mu_unlock(pr->mu);
 }
 
 void grpc_free_port_using_server(char *server, int port) {
   grpc_httpcli_context context;
   grpc_httpcli_request req;
+  grpc_httpcli_response rsp;
   freereq pr;
   char *path;
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
@@ -83,6 +84,7 @@
 
   memset(&pr, 0, sizeof(pr));
   memset(&req, 0, sizeof(req));
+  memset(&rsp, 0, sizeof(rsp));
 
   pr.pollset = gpr_malloc(grpc_pollset_size());
   grpc_pollset_init(pr.pollset, &pr.mu);
@@ -95,14 +97,18 @@
 
   grpc_httpcli_context_init(&context);
   grpc_httpcli_get(&exec_ctx, &context, pr.pollset, &req,
-                   GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), freed_port_from_server,
-                   &pr);
+                   GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
+                   grpc_closure_create(freed_port_from_server, &pr), &rsp);
   gpr_mu_lock(pr.mu);
   while (!pr.done) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, pr.pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1));
+    if (!GRPC_LOG_IF_ERROR(
+            "pollset_work",
+            grpc_pollset_work(&exec_ctx, pr.pollset, &worker,
+                              gpr_now(GPR_CLOCK_MONOTONIC),
+                              GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1)))) {
+      pr.done = 1;
+    }
   }
   gpr_mu_unlock(pr.mu);
 
@@ -111,6 +117,7 @@
   grpc_pollset_shutdown(&exec_ctx, pr.pollset, shutdown_closure);
   grpc_exec_ctx_finish(&exec_ctx);
   gpr_free(path);
+  grpc_http_response_destroy(&rsp);
 }
 
 typedef struct portreq {
@@ -120,19 +127,22 @@
   int retries;
   char *server;
   grpc_httpcli_context *ctx;
+  grpc_httpcli_response response;
 } portreq;
 
 static void got_port_from_server(grpc_exec_ctx *exec_ctx, void *arg,
-                                 const grpc_httpcli_response *response) {
+                                 grpc_error *error) {
   size_t i;
   int port = 0;
   portreq *pr = arg;
   int failed = 0;
+  grpc_httpcli_response *response = &pr->response;
 
-  if (!response) {
+  if (error != GRPC_ERROR_NONE) {
     failed = 1;
-    gpr_log(GPR_DEBUG,
-            "failed port pick from server: retrying [response=NULL]");
+    const char *msg = grpc_error_string(error);
+    gpr_log(GPR_DEBUG, "failed port pick from server: retrying [%s]", msg);
+    grpc_error_free_string(msg);
   } else if (response->status != 200) {
     failed = 1;
     gpr_log(GPR_DEBUG, "failed port pick from server: status=%d",
@@ -151,9 +161,12 @@
     pr->retries++;
     req.host = pr->server;
     req.http.path = "/get";
+    grpc_http_response_destroy(&pr->response);
+    memset(&pr->response, 0, sizeof(pr->response));
     grpc_httpcli_get(exec_ctx, pr->ctx, pr->pollset, &req,
-                     GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), got_port_from_server,
-                     pr);
+                     GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
+                     grpc_closure_create(got_port_from_server, pr),
+                     &pr->response);
     return;
   }
   GPR_ASSERT(response);
@@ -165,7 +178,7 @@
   GPR_ASSERT(port > 1024);
   gpr_mu_lock(pr->mu);
   pr->port = port;
-  grpc_pollset_kick(pr->pollset, NULL);
+  GRPC_LOG_IF_ERROR("pollset_kick", grpc_pollset_kick(pr->pollset, NULL));
   gpr_mu_unlock(pr->mu);
 }
 
@@ -193,18 +206,24 @@
 
   grpc_httpcli_context_init(&context);
   grpc_httpcli_get(&exec_ctx, &context, pr.pollset, &req,
-                   GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10), got_port_from_server,
-                   &pr);
+                   GRPC_TIMEOUT_SECONDS_TO_DEADLINE(10),
+                   grpc_closure_create(got_port_from_server, &pr),
+                   &pr.response);
   grpc_exec_ctx_finish(&exec_ctx);
   gpr_mu_lock(pr.mu);
   while (pr.port == -1) {
     grpc_pollset_worker *worker = NULL;
-    grpc_pollset_work(&exec_ctx, pr.pollset, &worker,
-                      gpr_now(GPR_CLOCK_MONOTONIC),
-                      GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1));
+    if (!GRPC_LOG_IF_ERROR(
+            "pollset_work",
+            grpc_pollset_work(&exec_ctx, pr.pollset, &worker,
+                              gpr_now(GPR_CLOCK_MONOTONIC),
+                              GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1)))) {
+      pr.port = 0;
+    }
   }
   gpr_mu_unlock(pr.mu);
 
+  grpc_http_response_destroy(&pr.response);
   grpc_httpcli_context_destroy(&context);
   grpc_pollset_shutdown(&exec_ctx, pr.pollset, shutdown_closure);
   grpc_exec_ctx_finish(&exec_ctx);
diff --git a/test/core/util/test_tcp_server.c b/test/core/util/test_tcp_server.c
index e39a957..27c16fc 100644
--- a/test/core/util/test_tcp_server.c
+++ b/test/core/util/test_tcp_server.c
@@ -46,7 +46,7 @@
 #include "test/core/util/port.h"
 
 static void on_server_destroyed(grpc_exec_ctx *exec_ctx, void *data,
-                                bool success) {
+                                grpc_error *error) {
   test_tcp_server *server = data;
   server->shutdown = 1;
 }
@@ -72,9 +72,12 @@
   addr.sin_port = htons((uint16_t)port);
   memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
 
-  server->tcp_server = grpc_tcp_server_create(&server->shutdown_complete);
-  port_added =
-      grpc_tcp_server_add_port(server->tcp_server, &addr, sizeof(addr));
+  grpc_error *error =
+      grpc_tcp_server_create(&server->shutdown_complete, &server->tcp_server);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
+  error = grpc_tcp_server_add_port(server->tcp_server, &addr, sizeof(addr),
+                                   &port_added);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
   GPR_ASSERT(port_added == port);
 
   grpc_tcp_server_start(&exec_ctx, server->tcp_server, &server->pollset, 1,
@@ -91,13 +94,14 @@
                    gpr_time_from_seconds(seconds, GPR_TIMESPAN));
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   gpr_mu_lock(server->mu);
-  grpc_pollset_work(&exec_ctx, server->pollset, &worker,
-                    gpr_now(GPR_CLOCK_MONOTONIC), deadline);
+  GRPC_LOG_IF_ERROR("pollset_work",
+                    grpc_pollset_work(&exec_ctx, server->pollset, &worker,
+                                      gpr_now(GPR_CLOCK_MONOTONIC), deadline));
   gpr_mu_unlock(server->mu);
   grpc_exec_ctx_finish(&exec_ctx);
 }
 
-static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, bool success) {}
+static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {}
 
 void test_tcp_server_destroy(test_tcp_server *server) {
   grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
diff --git a/test/cpp/common/auth_property_iterator_test.cc b/test/cpp/common/auth_property_iterator_test.cc
index 0e43d4e..66225ff 100644
--- a/test/cpp/common/auth_property_iterator_test.cc
+++ b/test/cpp/common/auth_property_iterator_test.cc
@@ -38,7 +38,7 @@
 #include "test/cpp/util/string_ref_helper.h"
 
 extern "C" {
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
 }
 
 using ::grpc::testing::ToString;
diff --git a/test/cpp/common/secure_auth_context_test.cc b/test/cpp/common/secure_auth_context_test.cc
index 0673613..b131452 100644
--- a/test/cpp/common/secure_auth_context_test.cc
+++ b/test/cpp/common/secure_auth_context_test.cc
@@ -38,7 +38,7 @@
 #include "test/cpp/util/string_ref_helper.h"
 
 extern "C" {
-#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/security/context/security_context.h"
 }
 
 using grpc::testing::ToString;
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index e3408bf..f52aa52 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -48,7 +48,7 @@
 #include <grpc/support/time.h>
 #include <gtest/gtest.h>
 
-#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/credentials/credentials.h"
 #include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
 #include "src/proto/grpc/testing/echo.grpc.pb.h"
 #include "test/core/util/port.h"
diff --git a/third_party/protobuf b/third_party/protobuf
index a1938b2..d5fb408 160000
--- a/third_party/protobuf
+++ b/third_party/protobuf
@@ -1 +1 @@
-Subproject commit a1938b2aa9ca86ce7ce50c27ff9737c1008d2a03
+Subproject commit d5fb408ddc281ffcadeb08699e65bb694656d0bd
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 212dfc3..f07c6df 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -807,6 +807,7 @@
 src/core/lib/iomgr/closure.h \
 src/core/lib/iomgr/endpoint.h \
 src/core/lib/iomgr/endpoint_pair.h \
+src/core/lib/iomgr/error.h \
 src/core/lib/iomgr/ev_poll_and_epoll_posix.h \
 src/core/lib/iomgr/ev_poll_posix.h \
 src/core/lib/iomgr/ev_posix.h \
@@ -885,15 +886,25 @@
 src/core/ext/transport/chttp2/transport/timeout_encoding.h \
 src/core/ext/transport/chttp2/transport/varint.h \
 src/core/ext/transport/chttp2/alpn/alpn.h \
-src/core/lib/security/auth_filters.h \
-src/core/lib/security/b64.h \
-src/core/lib/security/credentials.h \
-src/core/lib/security/handshake.h \
-src/core/lib/security/json_token.h \
-src/core/lib/security/jwt_verifier.h \
-src/core/lib/security/secure_endpoint.h \
-src/core/lib/security/security_connector.h \
-src/core/lib/security/security_context.h \
+src/core/lib/security/context/security_context.h \
+src/core/lib/security/credentials/composite/composite_credentials.h \
+src/core/lib/security/credentials/credentials.h \
+src/core/lib/security/credentials/fake/fake_credentials.h \
+src/core/lib/security/credentials/google_default/google_default_credentials.h \
+src/core/lib/security/credentials/iam/iam_credentials.h \
+src/core/lib/security/credentials/jwt/json_token.h \
+src/core/lib/security/credentials/jwt/jwt_credentials.h \
+src/core/lib/security/credentials/jwt/jwt_verifier.h \
+src/core/lib/security/credentials/oauth2/oauth2_credentials.h \
+src/core/lib/security/credentials/plugin/plugin_credentials.h \
+src/core/lib/security/credentials/ssl/ssl_credentials.h \
+src/core/lib/security/transport/auth_filters.h \
+src/core/lib/security/transport/handshake.h \
+src/core/lib/security/transport/secure_endpoint.h \
+src/core/lib/security/transport/security_connector.h \
+src/core/lib/security/transport/tsi_error.h \
+src/core/lib/security/util/b64.h \
+src/core/lib/security/util/json_util.h \
 src/core/lib/tsi/fake_transport_security.h \
 src/core/lib/tsi/ssl_transport_security.h \
 src/core/lib/tsi/ssl_types.h \
@@ -946,6 +957,7 @@
 src/core/lib/iomgr/endpoint.c \
 src/core/lib/iomgr/endpoint_pair_posix.c \
 src/core/lib/iomgr/endpoint_pair_windows.c \
+src/core/lib/iomgr/error.c \
 src/core/lib/iomgr/ev_poll_and_epoll_posix.c \
 src/core/lib/iomgr/ev_poll_posix.c \
 src/core/lib/iomgr/ev_posix.c \
@@ -1035,20 +1047,29 @@
 src/core/ext/transport/chttp2/transport/writing.c \
 src/core/ext/transport/chttp2/alpn/alpn.c \
 src/core/lib/http/httpcli_security_connector.c \
-src/core/lib/security/b64.c \
-src/core/lib/security/client_auth_filter.c \
-src/core/lib/security/credentials.c \
-src/core/lib/security/credentials_metadata.c \
-src/core/lib/security/credentials_posix.c \
-src/core/lib/security/credentials_win32.c \
-src/core/lib/security/google_default_credentials.c \
-src/core/lib/security/handshake.c \
-src/core/lib/security/json_token.c \
-src/core/lib/security/jwt_verifier.c \
-src/core/lib/security/secure_endpoint.c \
-src/core/lib/security/security_connector.c \
-src/core/lib/security/security_context.c \
-src/core/lib/security/server_auth_filter.c \
+src/core/lib/security/context/security_context.c \
+src/core/lib/security/credentials/composite/composite_credentials.c \
+src/core/lib/security/credentials/credentials.c \
+src/core/lib/security/credentials/credentials_metadata.c \
+src/core/lib/security/credentials/fake/fake_credentials.c \
+src/core/lib/security/credentials/google_default/credentials_posix.c \
+src/core/lib/security/credentials/google_default/credentials_win32.c \
+src/core/lib/security/credentials/google_default/google_default_credentials.c \
+src/core/lib/security/credentials/iam/iam_credentials.c \
+src/core/lib/security/credentials/jwt/json_token.c \
+src/core/lib/security/credentials/jwt/jwt_credentials.c \
+src/core/lib/security/credentials/jwt/jwt_verifier.c \
+src/core/lib/security/credentials/oauth2/oauth2_credentials.c \
+src/core/lib/security/credentials/plugin/plugin_credentials.c \
+src/core/lib/security/credentials/ssl/ssl_credentials.c \
+src/core/lib/security/transport/client_auth_filter.c \
+src/core/lib/security/transport/handshake.c \
+src/core/lib/security/transport/secure_endpoint.c \
+src/core/lib/security/transport/security_connector.c \
+src/core/lib/security/transport/server_auth_filter.c \
+src/core/lib/security/transport/tsi_error.c \
+src/core/lib/security/util/b64.c \
+src/core/lib/security/util/json_util.c \
 src/core/lib/surface/init_secure.c \
 src/core/lib/tsi/fake_transport_security.c \
 src/core/lib/tsi/ssl_transport_security.c \
diff --git a/tools/fuzzer/runners/http_fuzzer_test.sh b/tools/fuzzer/runners/http_request_fuzzer_test.sh
similarity index 95%
rename from tools/fuzzer/runners/http_fuzzer_test.sh
rename to tools/fuzzer/runners/http_request_fuzzer_test.sh
index d8dde14..250a761 100644
--- a/tools/fuzzer/runners/http_fuzzer_test.sh
+++ b/tools/fuzzer/runners/http_request_fuzzer_test.sh
@@ -42,4 +42,4 @@
   flags="-use_traces=1 $flags"
 fi
 
-bins/$config/http_fuzzer_test $flags fuzzer_output test/core/http/corpus
+bins/$config/http_request_fuzzer_test $flags fuzzer_output test/core/http/corpus
diff --git a/tools/fuzzer/runners/http_fuzzer_test.sh b/tools/fuzzer/runners/http_response_fuzzer_test.sh
similarity index 95%
copy from tools/fuzzer/runners/http_fuzzer_test.sh
copy to tools/fuzzer/runners/http_response_fuzzer_test.sh
index d8dde14..f747739 100644
--- a/tools/fuzzer/runners/http_fuzzer_test.sh
+++ b/tools/fuzzer/runners/http_response_fuzzer_test.sh
@@ -42,4 +42,4 @@
   flags="-use_traces=1 $flags"
 fi
 
-bins/$config/http_fuzzer_test $flags fuzzer_output test/core/http/corpus
+bins/$config/http_response_fuzzer_test $flags fuzzer_output test/core/http/corpus
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 65596de..97054c6 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -1212,7 +1212,7 @@
         cache=cache if not xml_report else None,
         add_env={'GRPC_TEST_PORT_SERVER': 'localhost:%d' % port_server_port})
     if resultset:
-      for k, v in resultset.iteritems():
+      for k, v in sorted(resultset.items()):
         num_runs, num_failures = _calculate_num_runs_failures(v)
         if num_failures == num_runs:  # what about infinite_runs???
           jobset.message('FAILED', k, do_newline=True)
diff --git a/tools/run_tests/sources_and_headers.json b/tools/run_tests/sources_and_headers.json
index 3866ebb..85c5a37 100644
--- a/tools/run_tests/sources_and_headers.json
+++ b/tools/run_tests/sources_and_headers.json
@@ -539,7 +539,8 @@
   {
     "deps": [
       "gpr", 
-      "gpr_test_util"
+      "gpr_test_util", 
+      "grpc"
     ], 
     "headers": [], 
     "language": "c", 
@@ -987,9 +988,9 @@
     ], 
     "headers": [], 
     "language": "c", 
-    "name": "http_fuzzer_test", 
+    "name": "http_parser_test", 
     "src": [
-      "test/core/http/fuzzer.c"
+      "test/core/http/parser_test.c"
     ], 
     "third_party": false, 
     "type": "target"
@@ -1003,9 +1004,25 @@
     ], 
     "headers": [], 
     "language": "c", 
-    "name": "http_parser_test", 
+    "name": "http_request_fuzzer_test", 
     "src": [
-      "test/core/http/parser_test.c"
+      "test/core/http/request_fuzzer.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "language": "c", 
+    "name": "http_response_fuzzer_test", 
+    "src": [
+      "test/core/http/response_fuzzer.c"
     ], 
     "third_party": false, 
     "type": "target"
@@ -2793,28 +2810,6 @@
   {
     "deps": [
       "gpr", 
-      "gpr_test_util", 
-      "grpc", 
-      "grpc++", 
-      "grpc++_test_util", 
-      "grpc_test_util", 
-      "grpc_zookeeper"
-    ], 
-    "headers": [
-      "src/proto/grpc/testing/echo.grpc.pb.h", 
-      "src/proto/grpc/testing/echo.pb.h"
-    ], 
-    "language": "c++", 
-    "name": "zookeeper_test", 
-    "src": [
-      "test/cpp/end2end/zookeeper_test.cc"
-    ], 
-    "third_party": false, 
-    "type": "target"
-  }, 
-  {
-    "deps": [
-      "gpr", 
       "grpc"
     ], 
     "headers": [], 
@@ -4041,9 +4036,26 @@
     ], 
     "headers": [], 
     "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
+    "name": "http_request_fuzzer_test_one_entry", 
     "src": [
-      "test/core/http/fuzzer.c", 
+      "test/core/http/request_fuzzer.c", 
+      "test/core/util/one_corpus_entry_fuzzer.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
+      "grpc_test_util"
+    ], 
+    "headers": [], 
+    "language": "c", 
+    "name": "http_response_fuzzer_test_one_entry", 
+    "src": [
+      "test/core/http/response_fuzzer.c", 
       "test/core/util/one_corpus_entry_fuzzer.c"
     ], 
     "third_party": false, 
@@ -4265,23 +4277,6 @@
   {
     "deps": [
       "gpr", 
-      "grpc"
-    ], 
-    "headers": [
-      "include/grpc/grpc_zookeeper.h"
-    ], 
-    "language": "c", 
-    "name": "grpc_zookeeper", 
-    "src": [
-      "include/grpc/grpc_zookeeper.h", 
-      "src/core/ext/resolver/zookeeper/zookeeper_resolver.c"
-    ], 
-    "third_party": false, 
-    "type": "lib"
-  }, 
-  {
-    "deps": [
-      "gpr", 
       "gpr_test_util", 
       "grpc", 
       "grpc_test_util", 
@@ -5645,6 +5640,7 @@
       "src/core/lib/iomgr/closure.h", 
       "src/core/lib/iomgr/endpoint.h", 
       "src/core/lib/iomgr/endpoint_pair.h", 
+      "src/core/lib/iomgr/error.h", 
       "src/core/lib/iomgr/ev_poll_and_epoll_posix.h", 
       "src/core/lib/iomgr/ev_poll_posix.h", 
       "src/core/lib/iomgr/ev_posix.h", 
@@ -5745,6 +5741,8 @@
       "src/core/lib/iomgr/endpoint_pair.h", 
       "src/core/lib/iomgr/endpoint_pair_posix.c", 
       "src/core/lib/iomgr/endpoint_pair_windows.c", 
+      "src/core/lib/iomgr/error.c", 
+      "src/core/lib/iomgr/error.h", 
       "src/core/lib/iomgr/ev_poll_and_epoll_posix.c", 
       "src/core/lib/iomgr/ev_poll_and_epoll_posix.h", 
       "src/core/lib/iomgr/ev_poll_posix.c", 
@@ -6056,15 +6054,25 @@
       "include/grpc/grpc_cronet.h", 
       "include/grpc/grpc_security.h", 
       "include/grpc/grpc_security_constants.h", 
-      "src/core/lib/security/auth_filters.h", 
-      "src/core/lib/security/b64.h", 
-      "src/core/lib/security/credentials.h", 
-      "src/core/lib/security/handshake.h", 
-      "src/core/lib/security/json_token.h", 
-      "src/core/lib/security/jwt_verifier.h", 
-      "src/core/lib/security/secure_endpoint.h", 
-      "src/core/lib/security/security_connector.h", 
-      "src/core/lib/security/security_context.h"
+      "src/core/lib/security/context/security_context.h", 
+      "src/core/lib/security/credentials/composite/composite_credentials.h", 
+      "src/core/lib/security/credentials/credentials.h", 
+      "src/core/lib/security/credentials/fake/fake_credentials.h", 
+      "src/core/lib/security/credentials/google_default/google_default_credentials.h", 
+      "src/core/lib/security/credentials/iam/iam_credentials.h", 
+      "src/core/lib/security/credentials/jwt/json_token.h", 
+      "src/core/lib/security/credentials/jwt/jwt_credentials.h", 
+      "src/core/lib/security/credentials/jwt/jwt_verifier.h", 
+      "src/core/lib/security/credentials/oauth2/oauth2_credentials.h", 
+      "src/core/lib/security/credentials/plugin/plugin_credentials.h", 
+      "src/core/lib/security/credentials/ssl/ssl_credentials.h", 
+      "src/core/lib/security/transport/auth_filters.h", 
+      "src/core/lib/security/transport/handshake.h", 
+      "src/core/lib/security/transport/secure_endpoint.h", 
+      "src/core/lib/security/transport/security_connector.h", 
+      "src/core/lib/security/transport/tsi_error.h", 
+      "src/core/lib/security/util/b64.h", 
+      "src/core/lib/security/util/json_util.h"
     ], 
     "language": "c", 
     "name": "grpc_secure", 
@@ -6073,29 +6081,48 @@
       "include/grpc/grpc_security.h", 
       "include/grpc/grpc_security_constants.h", 
       "src/core/lib/http/httpcli_security_connector.c", 
-      "src/core/lib/security/auth_filters.h", 
-      "src/core/lib/security/b64.c", 
-      "src/core/lib/security/b64.h", 
-      "src/core/lib/security/client_auth_filter.c", 
-      "src/core/lib/security/credentials.c", 
-      "src/core/lib/security/credentials.h", 
-      "src/core/lib/security/credentials_metadata.c", 
-      "src/core/lib/security/credentials_posix.c", 
-      "src/core/lib/security/credentials_win32.c", 
-      "src/core/lib/security/google_default_credentials.c", 
-      "src/core/lib/security/handshake.c", 
-      "src/core/lib/security/handshake.h", 
-      "src/core/lib/security/json_token.c", 
-      "src/core/lib/security/json_token.h", 
-      "src/core/lib/security/jwt_verifier.c", 
-      "src/core/lib/security/jwt_verifier.h", 
-      "src/core/lib/security/secure_endpoint.c", 
-      "src/core/lib/security/secure_endpoint.h", 
-      "src/core/lib/security/security_connector.c", 
-      "src/core/lib/security/security_connector.h", 
-      "src/core/lib/security/security_context.c", 
-      "src/core/lib/security/security_context.h", 
-      "src/core/lib/security/server_auth_filter.c", 
+      "src/core/lib/security/context/security_context.c", 
+      "src/core/lib/security/context/security_context.h", 
+      "src/core/lib/security/credentials/composite/composite_credentials.c", 
+      "src/core/lib/security/credentials/composite/composite_credentials.h", 
+      "src/core/lib/security/credentials/credentials.c", 
+      "src/core/lib/security/credentials/credentials.h", 
+      "src/core/lib/security/credentials/credentials_metadata.c", 
+      "src/core/lib/security/credentials/fake/fake_credentials.c", 
+      "src/core/lib/security/credentials/fake/fake_credentials.h", 
+      "src/core/lib/security/credentials/google_default/credentials_posix.c", 
+      "src/core/lib/security/credentials/google_default/credentials_win32.c", 
+      "src/core/lib/security/credentials/google_default/google_default_credentials.c", 
+      "src/core/lib/security/credentials/google_default/google_default_credentials.h", 
+      "src/core/lib/security/credentials/iam/iam_credentials.c", 
+      "src/core/lib/security/credentials/iam/iam_credentials.h", 
+      "src/core/lib/security/credentials/jwt/json_token.c", 
+      "src/core/lib/security/credentials/jwt/json_token.h", 
+      "src/core/lib/security/credentials/jwt/jwt_credentials.c", 
+      "src/core/lib/security/credentials/jwt/jwt_credentials.h", 
+      "src/core/lib/security/credentials/jwt/jwt_verifier.c", 
+      "src/core/lib/security/credentials/jwt/jwt_verifier.h", 
+      "src/core/lib/security/credentials/oauth2/oauth2_credentials.c", 
+      "src/core/lib/security/credentials/oauth2/oauth2_credentials.h", 
+      "src/core/lib/security/credentials/plugin/plugin_credentials.c", 
+      "src/core/lib/security/credentials/plugin/plugin_credentials.h", 
+      "src/core/lib/security/credentials/ssl/ssl_credentials.c", 
+      "src/core/lib/security/credentials/ssl/ssl_credentials.h", 
+      "src/core/lib/security/transport/auth_filters.h", 
+      "src/core/lib/security/transport/client_auth_filter.c", 
+      "src/core/lib/security/transport/handshake.c", 
+      "src/core/lib/security/transport/handshake.h", 
+      "src/core/lib/security/transport/secure_endpoint.c", 
+      "src/core/lib/security/transport/secure_endpoint.h", 
+      "src/core/lib/security/transport/security_connector.c", 
+      "src/core/lib/security/transport/security_connector.h", 
+      "src/core/lib/security/transport/server_auth_filter.c", 
+      "src/core/lib/security/transport/tsi_error.c", 
+      "src/core/lib/security/transport/tsi_error.h", 
+      "src/core/lib/security/util/b64.c", 
+      "src/core/lib/security/util/b64.h", 
+      "src/core/lib/security/util/json_util.c", 
+      "src/core/lib/security/util/json_util.h", 
       "src/core/lib/surface/init_secure.c"
     ], 
     "third_party": false, 
diff --git a/tools/run_tests/tests.json b/tools/run_tests/tests.json
index 4f11ceb..026c41d 100644
--- a/tools/run_tests/tests.json
+++ b/tools/run_tests/tests.json
@@ -47135,1213 +47135,6 @@
   }, 
   {
     "args": [
-      "test/core/http/corpus/0299ca2580e4398d170c4a336e0c33eb2cd9d427"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/05e613853d64a9669ea3cf41b0de777dc24931ba"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/069352518a1d1baa05f317c677d275cefda2ac97"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/0925527c9358b1e10ec0f0387cd99f35204d9a34"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/0c5b7c2569410b526605e308309a7f36574e530d"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/0ef3d0a84360bb5ad66274f1226f5cb273ecdbcf"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/1e1273f90187fdf5df3625764245610f86af6aa4"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/1fbc57d118f3733287e9a9d808bb8947b3260e55"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/24756c396bc72894fd720092bb6f9c03e66b469f"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/276def41311933421ae7a9ee42e906c85b6a4d3f"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/29daa75432381937fd005cb25e314e328de6e9f9"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/2a75204bc492084ad853682f8de3fb137d5907bc"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/2d34ba249b755a880525cf53c665633a5e359305"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/33f4ea0c7ea27c37d8f95cfa64d282370efdafd2"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/35554617ea6418bd43161fe9a2c337ed82d7ec5b"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/35f0c561297cfc840ddaeebb9fc61091f4eadece"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/3787bcc22ef645e665cc5f722b8a633af86de9cf"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/3953688866ccb3b4f371f1a858570d6afdb6452d"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/39b19c41ba537f37511eff7727733715db432e76"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/3e3c4756d5e40b5aa250954cbac86b826e70a7ac"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/3f03265921120c6ffa61b944e213e062a5538d4b"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/3fb034e66ee5494a67acae1b4e6ff64ba92a2046"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/466059ed07a0d55d6ad5e522c7d367cbf278eaf9"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/487725eb38511c79a9340bf4560a1411061fa6fa"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/48b9b205cae8ac21512a3f26f49fd53e21ee13c5"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/4b1f1f79a0bfa3f942479dd5f8edb59a7c257c55"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/5028c56a5116a186b7343ff59567b47347a0796d"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/533f62b3f495ce704babf3ee8d840f196a714dff"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/5892cbb284771fc9761caae37b19cd6e27dbc104"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/5aeab6e4f7c2a1c09d4ac0dbdb3beac4893607ee"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/5b6292bdf009b0daecbc90b85cca30a88c36eec5"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/5c1659b77678b41faa4fa13df7772dae3238d1c0"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/5c81f61621e29ec9c6a64ac3af9b3b216141618e"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/657368df512ca6294b9df16adf935a3f374a8be2"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/7fc4520094902ce2c760d70eaad5b674d2817337"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/81f59a12b458ec3604035cb962165c604d1355e6"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/8f41c50e88ee8c17ecad3d41d63d38fb12aca0b9"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/97c16de7fe3c390a2e6c09ff5c28f17d5c67542c"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/97e4499d450c95660de86747f527e670f2012548"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/9a996857196e0998a1278994a9bab3d35526e7f1"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/9b7e00049ec356ecd84b1747e4e1941140139ae8"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/9f0c38ec455cc363369b3674a2d32bc21c206de1"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/a1dc7bc235e46eb21d91084d7b52d5ff9f45df85"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/aa3bbb876eafa8ad8ca4ff2eabc6dd94341d2441"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/ae8ba95d7dbe99926a8f5bfd80347fd6a4b616a0"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/b04fea5c041c707db0ad9c09a81672557b52cc47"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/c4acff8aa2ff886f35439f72625d05002990c940"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/c55ce9995b002e88a102ae2891a71e8bacb346c8"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/ca5a0c00b8969310acb73d15ad0d0c602f1bd0c2"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/cce734f1b263de6994f7950e0df7bf0c81449f70"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/d39c8ee11a697634a09b309460c0bbd967e7effa"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/d4c3e4cf5d035596433c30eaabbd2b2925f4b453"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/d51f7fcc089f269c7afecaaca51966bab5fde629"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/d936dad71c129cf659097dc3db64550c4dd467f4"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/e275b0466a8fb8d9e0e15856e343ddc7112ae66b"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/e5c364b205855a2991ce07482aebb2a3a6147089"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/ee2077e08c3cfccd9bd82adb574ac4fc7d429afb"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/fc5d4b9117ba9e87388174aee4f4970bdfe8d066"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/fdeb2c7daa9e7704f67e141106384e6dd0042c0b"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/request1.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/request2.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/request3.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/request4.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/request5.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/response1.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/response2.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/response3.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/response4.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/response5.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/response6.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
-      "test/core/http/corpus/toolong.txt"
-    ], 
-    "ci_platforms": [
-      "linux"
-    ], 
-    "cpu_cost": 0.1, 
-    "exclude_configs": [], 
-    "flaky": false, 
-    "language": "c", 
-    "name": "http_fuzzer_test_one_entry", 
-    "platforms": [
-      "linux"
-    ], 
-    "uses_polling": false
-  }, 
-  {
-    "args": [
       "test/core/json/corpus/006d552e952c42b5340baaeb85c2cb80c81e78dd"
     ], 
     "ci_platforms": [
diff --git a/vsprojects/buildtests_c.sln b/vsprojects/buildtests_c.sln
index be8b5d4..95a7f17 100644
--- a/vsprojects/buildtests_c.sln
+++ b/vsprojects/buildtests_c.sln
@@ -394,6 +394,7 @@
         	lib = "False"
 	EndProjectSection
 	ProjectSection(ProjectDependencies) = postProject
+		{29D16885-7228-4C31-81ED-5F9187C7F2A9} = {29D16885-7228-4C31-81ED-5F9187C7F2A9}
 		{EAB0A629-17A9-44DB-B5FF-E91A721FE037} = {EAB0A629-17A9-44DB-B5FF-E91A721FE037}
 		{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792} = {B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}
 	EndProjectSection
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj b/vsprojects/vcxproj/grpc/grpc.vcxproj
index a20d386..99bb0aa 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj
@@ -316,6 +316,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.h" />
@@ -394,15 +395,25 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\timeout_encoding.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\transport\varint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\alpn\alpn.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\auth_filters.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\b64.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\handshake.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\json_token.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\jwt_verifier.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\secure_endpoint.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\security_connector.h" />
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\security_context.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\context\security_context.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\composite\composite_credentials.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\credentials.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\fake\fake_credentials.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\google_default\google_default_credentials.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\iam\iam_credentials.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\json_token.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\jwt_credentials.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\jwt_verifier.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\oauth2\oauth2_credentials.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\plugin\plugin_credentials.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\ssl\ssl_credentials.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\auth_filters.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\handshake.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\secure_endpoint.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\security_connector.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\tsi_error.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\util\b64.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\util\json_util.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\tsi\fake_transport_security.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\tsi\ssl_transport_security.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\tsi\ssl_types.h" />
@@ -475,6 +486,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair_windows.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\error.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.c">
@@ -653,33 +666,51 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\http\httpcli_security_connector.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\b64.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\context\security_context.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\client_auth_filter.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\composite\composite_credentials.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\credentials.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials_metadata.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\credentials_metadata.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials_posix.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\fake\fake_credentials.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials_win32.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\google_default\credentials_posix.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\google_default_credentials.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\google_default\credentials_win32.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\handshake.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\google_default\google_default_credentials.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\json_token.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\iam\iam_credentials.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\jwt_verifier.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\json_token.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\secure_endpoint.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\jwt_credentials.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\security_connector.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\jwt_verifier.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\security_context.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\oauth2\oauth2_credentials.c">
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\server_auth_filter.c">
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\plugin\plugin_credentials.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\ssl\ssl_credentials.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\client_auth_filter.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\handshake.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\secure_endpoint.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\security_connector.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\server_auth_filter.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\tsi_error.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\util\b64.c">
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\util\json_util.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\init_secure.c">
     </ClCompile>
diff --git a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
index d546517..bfaa41e 100644
--- a/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc/grpc.vcxproj.filters
@@ -55,6 +55,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair_windows.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\error.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
@@ -322,47 +325,74 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\http\httpcli_security_connector.c">
       <Filter>src\core\lib\http</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\b64.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\context\security_context.c">
+      <Filter>src\core\lib\security\context</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\client_auth_filter.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\composite\composite_credentials.c">
+      <Filter>src\core\lib\security\credentials\composite</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\credentials.c">
+      <Filter>src\core\lib\security\credentials</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials_metadata.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\credentials_metadata.c">
+      <Filter>src\core\lib\security\credentials</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials_posix.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\fake\fake_credentials.c">
+      <Filter>src\core\lib\security\credentials\fake</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials_win32.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\google_default\credentials_posix.c">
+      <Filter>src\core\lib\security\credentials\google_default</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\google_default_credentials.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\google_default\credentials_win32.c">
+      <Filter>src\core\lib\security\credentials\google_default</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\handshake.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\google_default\google_default_credentials.c">
+      <Filter>src\core\lib\security\credentials\google_default</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\json_token.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\iam\iam_credentials.c">
+      <Filter>src\core\lib\security\credentials\iam</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\jwt_verifier.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\json_token.c">
+      <Filter>src\core\lib\security\credentials\jwt</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\secure_endpoint.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\jwt_credentials.c">
+      <Filter>src\core\lib\security\credentials\jwt</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\security_connector.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\jwt_verifier.c">
+      <Filter>src\core\lib\security\credentials\jwt</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\security_context.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\oauth2\oauth2_credentials.c">
+      <Filter>src\core\lib\security\credentials\oauth2</Filter>
     </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\server_auth_filter.c">
-      <Filter>src\core\lib\security</Filter>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\plugin\plugin_credentials.c">
+      <Filter>src\core\lib\security\credentials\plugin</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\credentials\ssl\ssl_credentials.c">
+      <Filter>src\core\lib\security\credentials\ssl</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\client_auth_filter.c">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\handshake.c">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\secure_endpoint.c">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\security_connector.c">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\server_auth_filter.c">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\transport\tsi_error.c">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\util\b64.c">
+      <Filter>src\core\lib\security\util</Filter>
+    </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\security\util\json_util.c">
+      <Filter>src\core\lib\security\util</Filter>
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\surface\init_secure.c">
       <Filter>src\core\lib\surface</Filter>
@@ -653,6 +683,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
@@ -887,32 +920,62 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\ext\transport\chttp2\alpn\alpn.h">
       <Filter>src\core\ext\transport\chttp2\alpn</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\auth_filters.h">
-      <Filter>src\core\lib\security</Filter>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\context\security_context.h">
+      <Filter>src\core\lib\security\context</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\b64.h">
-      <Filter>src\core\lib\security</Filter>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\composite\composite_credentials.h">
+      <Filter>src\core\lib\security\credentials\composite</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials.h">
-      <Filter>src\core\lib\security</Filter>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\credentials.h">
+      <Filter>src\core\lib\security\credentials</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\handshake.h">
-      <Filter>src\core\lib\security</Filter>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\fake\fake_credentials.h">
+      <Filter>src\core\lib\security\credentials\fake</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\json_token.h">
-      <Filter>src\core\lib\security</Filter>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\google_default\google_default_credentials.h">
+      <Filter>src\core\lib\security\credentials\google_default</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\jwt_verifier.h">
-      <Filter>src\core\lib\security</Filter>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\iam\iam_credentials.h">
+      <Filter>src\core\lib\security\credentials\iam</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\secure_endpoint.h">
-      <Filter>src\core\lib\security</Filter>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\json_token.h">
+      <Filter>src\core\lib\security\credentials\jwt</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\security_connector.h">
-      <Filter>src\core\lib\security</Filter>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\jwt_credentials.h">
+      <Filter>src\core\lib\security\credentials\jwt</Filter>
     </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\security_context.h">
-      <Filter>src\core\lib\security</Filter>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\jwt\jwt_verifier.h">
+      <Filter>src\core\lib\security\credentials\jwt</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\oauth2\oauth2_credentials.h">
+      <Filter>src\core\lib\security\credentials\oauth2</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\plugin\plugin_credentials.h">
+      <Filter>src\core\lib\security\credentials\plugin</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\credentials\ssl\ssl_credentials.h">
+      <Filter>src\core\lib\security\credentials\ssl</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\auth_filters.h">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\handshake.h">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\secure_endpoint.h">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\security_connector.h">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\transport\tsi_error.h">
+      <Filter>src\core\lib\security\transport</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\util\b64.h">
+      <Filter>src\core\lib\security\util</Filter>
+    </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\security\util\json_util.h">
+      <Filter>src\core\lib\security\util</Filter>
     </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\tsi\fake_transport_security.h">
       <Filter>src\core\lib\tsi</Filter>
@@ -1148,6 +1211,42 @@
     <Filter Include="src\core\lib\security">
       <UniqueIdentifier>{c4661d64-349f-01c1-1ba8-0602f9047595}</UniqueIdentifier>
     </Filter>
+    <Filter Include="src\core\lib\security\context">
+      <UniqueIdentifier>{187b52e3-bc78-6c62-3e68-4eb19a257661}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\credentials">
+      <UniqueIdentifier>{c8af33b1-f786-001d-3e92-140872dc9829}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\credentials\composite">
+      <UniqueIdentifier>{197ed135-5f84-9f6a-6751-38dc5e9dd38c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\credentials\fake">
+      <UniqueIdentifier>{6d391299-53d7-ee6a-55aa-d4c46cd86e82}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\credentials\google_default">
+      <UniqueIdentifier>{412c7418-e90a-de77-5705-7890ba960911}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\credentials\iam">
+      <UniqueIdentifier>{718f826c-994b-7dd4-3042-0e999c5c22ba}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\credentials\jwt">
+      <UniqueIdentifier>{ab21bcdf-de99-5838-699a-19ecb0c4aa14}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\credentials\oauth2">
+      <UniqueIdentifier>{f47a7a32-3166-b899-3622-f062f372feea}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\credentials\plugin">
+      <UniqueIdentifier>{46120bcc-03e3-1aaa-fc61-9cef786bd70c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\credentials\ssl">
+      <UniqueIdentifier>{9d7802bc-d459-1a9b-3c97-868cddcca1d1}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\transport">
+      <UniqueIdentifier>{b22e611f-8272-9914-24a5-8107ebf51eeb}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="src\core\lib\security\util">
+      <UniqueIdentifier>{fcd7b397-aadd-556a-8aae-0cb7c893fbe0}</UniqueIdentifier>
+    </Filter>
     <Filter Include="src\core\lib\surface">
       <UniqueIdentifier>{a21971fb-304f-da08-b1b2-7bd8df8ac373}</UniqueIdentifier>
     </Filter>
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
index 09748f0..eef5a13 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj
@@ -304,6 +304,7 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\closure.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h" />
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.h" />
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_posix.h" />
@@ -450,6 +451,8 @@
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair_windows.c">
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\error.c">
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.c">
     </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_posix.c">
diff --git a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
index a85bfee..e38e8ca 100644
--- a/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
+++ b/vsprojects/vcxproj/grpc_unsecure/grpc_unsecure.vcxproj.filters
@@ -58,6 +58,9 @@
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair_windows.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
+    <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\error.c">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClCompile>
     <ClCompile Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.c">
       <Filter>src\core\lib\iomgr</Filter>
     </ClCompile>
@@ -575,6 +578,9 @@
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\endpoint_pair.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
+    <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\error.h">
+      <Filter>src\core\lib\iomgr</Filter>
+    </ClInclude>
     <ClInclude Include="$(SolutionDir)\..\src\core\lib\iomgr\ev_poll_and_epoll_posix.h">
       <Filter>src\core\lib\iomgr</Filter>
     </ClInclude>
diff --git a/vsprojects/vcxproj/test/gpr_load_file_test/gpr_load_file_test.vcxproj b/vsprojects/vcxproj/test/gpr_load_file_test/gpr_load_file_test.vcxproj
index 4182969..504ef88 100644
--- a/vsprojects/vcxproj/test/gpr_load_file_test/gpr_load_file_test.vcxproj
+++ b/vsprojects/vcxproj/test/gpr_load_file_test/gpr_load_file_test.vcxproj
@@ -162,6 +162,9 @@
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
+      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
+    </ProjectReference>
     <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
       <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
     </ProjectReference>