upon ech config mismatch, report retry_config to the application iff it is applicable
diff --git a/lib/picotls.c b/lib/picotls.c
index c61caaa..d810819 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -155,6 +155,16 @@
     struct st_ptls_signature_algorithms_t signature_algorithms;
 };
 
+struct decoded_ech_config_t {
+    uint8_t id;
+    ptls_hpke_kem_t *kem;
+    ptls_iovec_t public_key;
+    ptls_hpke_cipher_suite_t *cipher;
+    uint8_t max_name_length;
+    ptls_iovec_t public_name;
+    ptls_iovec_t bytes;
+};
+
 /**
  * Variables to be used for generating ClientHello; see draft-ietf-tls-esni section 4.
  */
@@ -970,38 +980,32 @@
 }
 
 /**
- * Decodes one ECHConfigContents (tls-esni-15 section 4). `kem` and `cipher` may be NULL even when the function returns zero, if the
- * corresponding entries are not found.
+ * Decodes one ECHConfigContents (tls-esni-15 section 4). `decoded->kem` and `cipher` may be NULL even when the function returns
+ * zero, if the corresponding entries are not found.
  */
-static int decode_one_ech_config(ptls_hpke_kem_t **kems, ptls_hpke_cipher_suite_t **ciphers, uint8_t *config_id,
-                                 ptls_hpke_kem_t **kem, ptls_iovec_t *public_key, ptls_hpke_cipher_suite_t **cipher,
-                                 uint8_t *max_name_length, ptls_iovec_t *public_name, const uint8_t **src, const uint8_t *const end)
+static int decode_one_ech_config(ptls_hpke_kem_t **kems, ptls_hpke_cipher_suite_t **ciphers, struct decoded_ech_config_t *decoded,
+                                 const uint8_t **src, const uint8_t *const end)
 {
     int ret;
 
-    *config_id = 0;
-    *kem = NULL;
-    *public_key = ptls_iovec_init(NULL, 0);
-    *cipher = NULL;
-    *max_name_length = 0;
-    *public_name = ptls_iovec_init(NULL, 0);
+    *decoded = (struct decoded_ech_config_t){0};
 
     if (*src == end) {
         ret = PTLS_ALERT_DECODE_ERROR;
         goto Exit;
     }
-    *config_id = *(*src)++;
+    decoded->id = *(*src)++;
     uint16_t kem_id;
     if ((ret = ptls_decode16(&kem_id, src, end)) != 0)
         goto Exit;
     for (size_t i = 0; kems[i] != NULL; ++i) {
         if (kems[i]->id == kem_id) {
-            *kem = kems[i];
+            decoded->kem = kems[i];
             break;
         }
     }
     ptls_decode_open_block(*src, end, 2, {
-        *public_key = ptls_iovec_init(*src, end - *src);
+        decoded->public_key = ptls_iovec_init(*src, end - *src);
         *src = end;
     });
     ptls_decode_open_block(*src, end, 2, {
@@ -1012,10 +1016,10 @@
                 goto Exit;
             if ((ret = ptls_decode16(&aead_id, src, end)) != 0)
                 goto Exit;
-            if (*cipher == NULL) {
+            if (decoded->cipher == NULL) {
                 for (size_t i = 0; ciphers[i] != NULL; ++i) {
                     if (ciphers[i]->id.kdf == kdf_id && ciphers[i]->id.aead == aead_id) {
-                        *cipher = ciphers[i];
+                        decoded->cipher = ciphers[i];
                         break;
                     }
                 }
@@ -1026,9 +1030,9 @@
         ret = PTLS_ALERT_DECODE_ERROR;
         goto Exit;
     }
-    *max_name_length = *(*src)++;
+    decoded->max_name_length = *(*src)++;
     ptls_decode_open_block(*src, end, 1, {
-        *public_name = ptls_iovec_init(*src, end - *src);
+        decoded->public_name = ptls_iovec_init(*src, end - *src);
         *src = end;
     });
     ptls_decode_block(*src, end, 2, {
@@ -1039,8 +1043,8 @@
             ptls_decode_open_block(*src, end, 2, { *src = end; });
             /* if a critital extension is found, indicate that the config cannot be used */
             if ((type & 0x8000) != 0) {
-                *kem = NULL;
-                *cipher = NULL;
+                decoded->kem = NULL;
+                decoded->cipher = NULL;
             }
         }
     });
@@ -1049,46 +1053,12 @@
     return ret;
 }
 
-static struct st_ptls_ech_client_t *client_setup_ech_instantiate(uint8_t config_id, ptls_hpke_kem_t *kem, ptls_iovec_t public_key,
-                                                                 ptls_hpke_cipher_suite_t *cipher, uint8_t max_name_length,
-                                                                 ptls_iovec_t public_name, ptls_iovec_t ech_config)
-{
-    struct st_ptls_ech_client_t *ech;
-    ptls_buffer_t infobuf;
-    uint8_t infobuf_smallbuf[256];
-    int ret;
-
-    ptls_buffer_init(&infobuf, infobuf_smallbuf, sizeof(infobuf_smallbuf));
-
-    if ((ech = malloc(offsetof(struct st_ptls_ech_client_t, public_name) + public_name.len + 1)) == NULL) {
-        ret = PTLS_ERROR_NO_MEMORY;
-        goto Exit;
-    }
-    *ech = (struct st_ptls_ech_client_t){.config_id = config_id, .cipher = cipher, .max_name_length = max_name_length};
-    memcpy(ech->public_name, public_name.base, public_name.len);
-    ech->public_name[public_name.len] = '\0';
-
-    ptls_buffer_pushv(&infobuf, ech_info_prefix.base, ech_info_prefix.len);
-    ptls_buffer_pushv(&infobuf, ech_config.base, ech_config.len);
-
-    ret = ptls_hpke_setup_base_s(kem, cipher, &ech->enc, &ech->aead, public_key, ptls_iovec_init(infobuf.base, infobuf.off));
-
-Exit:
-    if (ret != 0) {
-        if (ech != NULL)
-            client_free_ech(ech);
-        ech = NULL;
-    }
-    return ech;
-}
-
-static int decode_ech_config_list(ptls_context_t *ctx, ptls_iovec_t config_list, struct st_ptls_ech_client_t **ech)
+static int decode_ech_config_list(ptls_context_t *ctx, struct decoded_ech_config_t *decoded, ptls_iovec_t config_list)
 {
     const uint8_t *src = config_list.base, *const end = src + config_list.len;
-    int ret;
+    int match_found = 0, ret;
 
-    if (ech != NULL)
-        *ech = NULL;
+    *decoded = (struct decoded_ech_config_t){0};
 
     ptls_decode_block(src, end, 2, {
         do {
@@ -1099,18 +1069,14 @@
             ptls_decode_open_block(src, end, 2, {
                 /* If the block is the one that we recognize, parse it, then adopt if if possible. Otherwise, skip. */
                 if (version == PTLS_ECH_CONFIG_VERSION) {
-                    uint8_t config_id;
-                    ptls_hpke_kem_t *kem;
-                    ptls_iovec_t public_key;
-                    ptls_hpke_cipher_suite_t *cipher;
-                    uint8_t max_name_length;
-                    ptls_iovec_t public_name;
-                    if ((ret = decode_one_ech_config(ctx->ech.kems, ctx->ech.ciphers, &config_id, &kem, &public_key, &cipher,
-                                                     &max_name_length, &public_name, &src, end)) != 0)
+                    struct decoded_ech_config_t thisconf;
+                    if ((ret = decode_one_ech_config(ctx->ech.kems, ctx->ech.ciphers, &thisconf, &src, end)) != 0)
                         goto Exit;
-                    if (kem != NULL && cipher != NULL && ech != NULL && *ech == NULL)
-                        *ech = client_setup_ech_instantiate(config_id, kem, public_key, cipher, max_name_length, public_name,
-                                                            ptls_iovec_init(config_start, end - config_start));
+                    if (!match_found && thisconf.kem != NULL && thisconf.cipher != NULL) {
+                        *decoded = thisconf;
+                        decoded->bytes = ptls_iovec_init(config_start, end - config_start);
+                        match_found = 1;
+                    }
                 } else {
                     src = end;
                 }
@@ -1120,13 +1086,46 @@
     ret = 0;
 
 Exit:
-    if (ret != 0 && ech != NULL && *ech != NULL) {
-        client_free_ech(*ech);
-        *ech = NULL;
-    }
+    if (ret != 0)
+        *decoded = (struct decoded_ech_config_t){0};
     return ret;
 }
 
+static struct st_ptls_ech_client_t *client_instantiate_ech(struct decoded_ech_config_t *decoded)
+{
+    if (decoded->kem == NULL || decoded->cipher == NULL)
+        return NULL;
+
+    struct st_ptls_ech_client_t *ech;
+    ptls_buffer_t infobuf;
+    uint8_t infobuf_smallbuf[256];
+    int ret;
+
+    ptls_buffer_init(&infobuf, infobuf_smallbuf, sizeof(infobuf_smallbuf));
+
+    if ((ech = malloc(offsetof(struct st_ptls_ech_client_t, public_name) + decoded->public_name.len + 1)) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+    *ech = (struct st_ptls_ech_client_t){.config_id = decoded->id, .cipher = decoded->cipher, .max_name_length = decoded->max_name_length};
+    memcpy(ech->public_name, decoded->public_name.base, decoded->public_name.len);
+    ech->public_name[decoded->public_name.len] = '\0';
+
+    ptls_buffer_pushv(&infobuf, ech_info_prefix.base, ech_info_prefix.len);
+    ptls_buffer_pushv(&infobuf, decoded->bytes.base, decoded->bytes.len);
+
+    ret = ptls_hpke_setup_base_s(decoded->kem, decoded->cipher, &ech->enc, &ech->aead, decoded->public_key,
+                                 ptls_iovec_init(infobuf.base, infobuf.off));
+
+Exit:
+    if (ret != 0) {
+        if (ech != NULL)
+            client_free_ech(ech);
+        ech = NULL;
+    }
+    return ech;
+}
+
 #define ECH_CONFIRMATION_SERVER_HELLO "ech accept confirmation"
 #define ECH_CONFIRMATION_HRR "hrr ech accept confirmation"
 static int ech_calc_confirmation(ptls_key_schedule_t *sched, void *dst, const uint8_t *inner_random, const char *label,
@@ -2232,8 +2231,9 @@
         /* try to use ECH (ignore broken ECHConfigList; it is delivered insecurely) */
         if (!is_second_flight && tls->ctx->ech.ciphers != NULL && tls->ctx->ech.kems != NULL &&
             properties->client.ech.configs.len != 0) {
-            decode_ech_config_list(tls->ctx, properties->client.ech.configs, &tls->client.ech);
-            if (tls->client.ech != NULL)
+            struct decoded_ech_config_t decoded;
+            decode_ech_config_list(tls->ctx, &decoded, properties->client.ech.configs);
+            if ((tls->client.ech = client_instantiate_ech(&decoded)) != NULL)
                 tls->ctx->random_bytes(tls->client_random.inner, PTLS_HELLO_RANDOM_SIZE);
         }
         /* setup resumption-related data. If successful, resumption_secret becomes a non-zero value. */
@@ -2792,7 +2792,8 @@
             server_offered_cert_type = *src;
             src = end;
             break;
-        case PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO:
+        case PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO: {
+            struct decoded_ech_config_t decoded;
             if (src == end) {
                 ret = PTLS_ALERT_DECODE_ERROR;
                 goto Exit;
@@ -2801,9 +2802,10 @@
                 ret = PTLS_ALERT_UNSUPPORTED_EXTENSION;
                 goto Exit;
             }
-            if ((ret = decode_ech_config_list(tls->ctx, ptls_iovec_init(src, end - src), NULL)) != 0)
+            if ((ret = decode_ech_config_list(tls->ctx, &decoded, ptls_iovec_init(src, end - src))) != 0)
                 goto Exit;
-            if (properties != NULL && properties->client.ech.retry_configs != NULL) {
+            if (decoded.kem != NULL && decoded.cipher != NULL && properties != NULL &&
+                properties->client.ech.retry_configs != NULL) {
                 if ((properties->client.ech.retry_configs->base = malloc(end - src)) == NULL) {
                     ret = PTLS_ERROR_NO_MEMORY;
                     goto Exit;
@@ -2811,7 +2813,7 @@
                 *properties->client.ech.retry_configs = ptls_iovec_init(src, end - src);
             }
             src = end;
-            break;
+        } break;
         default:
             if (should_collect_unknown_extension(tls, properties, type)) {
                 if (unknown_extensions == &no_unknown_extensions) {
diff --git a/t/picotls.c b/t/picotls.c
index 31145db..835f9ed 100644
--- a/t/picotls.c
+++ b/t/picotls.c
@@ -502,32 +502,27 @@
     static ptls_hpke_kem_t p256 = {PTLS_HPKE_KEM_P256_SHA256}, *kems[] = {&p256, NULL};
     static ptls_hpke_cipher_suite_t aes128gcmsha256 = {{PTLS_HPKE_HKDF_SHA256, PTLS_HPKE_AEAD_AES_128_GCM}},
                                     *ciphers[] = {&aes128gcmsha256, NULL};
-    uint8_t config_id, max_name_length;
-    ptls_hpke_kem_t *kem;
-    ptls_hpke_cipher_suite_t *cipher;
-    ptls_iovec_t public_key, public_name;
+    struct decoded_ech_config_t decoded;
 
     { /* broken list */
         const uint8_t *src = (const uint8_t *)"a", *end = src + 1;
-        int ret =
-            decode_one_ech_config(kems, ciphers, &config_id, &kem, &public_key, &cipher, &max_name_length, &public_name, &src, end);
+        int ret = decode_one_ech_config(kems, ciphers, &decoded, &src, end);
         ok(ret == PTLS_ALERT_DECODE_ERROR);
     }
 
     {
         ptls_iovec_t input = ptls_iovec_init(ECH_CONFIG_LIST, sizeof(ECH_CONFIG_LIST) - 1);
         const uint8_t *src = input.base + 6 /* dive into ECHConfigContents */, *const end = input.base + input.len;
-        int ret =
-            decode_one_ech_config(kems, ciphers, &config_id, &kem, &public_key, &cipher, &max_name_length, &public_name, &src, end);
+        int ret = decode_one_ech_config(kems, ciphers, &decoded, &src, end);
         ok(ret == 0);
-        ok(config_id == 0x12);
-        ok(kem == &p256);
-        ok(public_key.len == 65);
-        ok(public_key.base == input.base + 11);
-        ok(cipher == &aes128gcmsha256);
-        ok(max_name_length == 64);
-        ok(public_name.len == sizeof("example.com") - 1);
-        ok(memcmp(public_name.base, "example.com", sizeof("example.com") - 1) == 0);
+        ok(decoded.id == 0x12);
+        ok(decoded.kem == &p256);
+        ok(decoded.public_key.len == 65);
+        ok(decoded.public_key.base == input.base + 11);
+        ok(decoded.cipher == &aes128gcmsha256);
+        ok(decoded.max_name_length == 64);
+        ok(decoded.public_name.len == sizeof("example.com") - 1);
+        ok(memcmp(decoded.public_name.base, "example.com", sizeof("example.com") - 1) == 0);
     }
 }