diff --git a/changelogs/unreleased/gh-7968-mp-check-diag.md b/changelogs/unreleased/gh-7968-mp-check-diag.md
new file mode 100644
index 0000000000000000000000000000000000000000..1e2345b8f47dc4a0f6b49073e0e5c7792936504e
--- /dev/null
+++ b/changelogs/unreleased/gh-7968-mp-check-diag.md
@@ -0,0 +1,7 @@
+## feature/lua/msgpack
+
+* Improved error reporting for `msgpack.decode`. Now, an error raised by
+  `mgpack.decode` has a detailed error message and the offset in the input
+  data. If `msgpack.decode` failed to unpack a MsgPack extension, it also
+  includes the error cause pointing to the error in the extension data
+  (gh-7986).
diff --git a/src/box/mp_error.cc b/src/box/mp_error.cc
index a6219433e92ab78aff71efcf4b91caf352b305f8..511c1ecd1daf1ffc9bc7dfdf6bf979865416c66a 100644
--- a/src/box/mp_error.cc
+++ b/src/box/mp_error.cc
@@ -42,6 +42,7 @@
 #include "mp_extension_types.h"
 #include "fiber.h"
 #include "ssl_error.h"
+#include "trivia/util.h"
 
 /**
  * MP_ERROR format:
@@ -207,13 +208,21 @@ static struct error *
 error_build(struct mp_error *mp_error)
 {
 	struct error *err = NULL;
-	if (mp_error->type == NULL || mp_error->message == NULL ||
-	    mp_error->file == NULL) {
+	if (mp_error->type == NULL) {
 		diag_set(ClientError, ER_INVALID_MSGPACK,
-			 "Missing mandatory error fields");
+			 "MP_ERROR_TYPE is missing");
+		return NULL;
+	}
+	if (mp_error->message == NULL) {
+		diag_set(ClientError, ER_INVALID_MSGPACK,
+			 "MP_ERROR_MESSAGE is missing");
+		return NULL;
+	}
+	if (mp_error->file == NULL) {
+		diag_set(ClientError, ER_INVALID_MSGPACK,
+			 "MP_ERROR_FILE is missing");
 		return NULL;
 	}
-
 	if (strcmp(mp_error->type, "ClientError") == 0) {
 		err = new ClientError();
 	} else if (strcmp(mp_error->type, "CustomError") == 0) {
@@ -261,44 +270,43 @@ error_build(struct mp_error *mp_error)
 	return err;
 }
 
-static inline const char *
-region_strdup(struct region *region, const char *str, uint32_t len)
-{
-	char *res = (char *)region_alloc(region, len + 1);
-	if (res == NULL) {
-		diag_set(OutOfMemory, len + 1, "region_alloc", "res");
-		return NULL;
-	}
-	memcpy(res, str, len);
-	res[len] = 0;
-	return res;
-}
-
 static inline const char *
 mp_decode_and_copy_str(const char **data, struct region *region)
 {
-	if (mp_typeof(**data) != MP_STR) {
-		diag_set(ClientError, ER_INVALID_MSGPACK,
-			 "Invalid MP_ERROR MsgPack format");
-		return NULL;
-	}
 	uint32_t str_len;
 	const char *str = mp_decode_str(data, &str_len);
-	return region_strdup(region, str, str_len);;
+	char *copy = (char *)xregion_alloc(region, str_len + 1);
+	memcpy(copy, str, str_len);
+	copy[str_len] = '\0';
+	return copy;
 }
 
+/**
+ * Helper macro for checking that MsgPack data has a particular type.
+ * Sets diag and returns -1 on error.
+ */
+#define CHECK_MP_TYPE(data, type, what) ({				\
+	int rc = 0;							\
+	if (mp_typeof(**(data)) != (type)) {				\
+		diag_set(ClientError, ER_INVALID_MSGPACK,		\
+			 what " must be " #type);			\
+		rc = -1;						\
+	}								\
+	rc;								\
+})
+
 static int
 mp_decode_error_fields(const char **data, struct mp_error *mp_err,
 		       struct region *region)
 {
-	if (mp_typeof(**data) != MP_MAP)
+	if (CHECK_MP_TYPE(data, MP_MAP, "MP_ERROR_FIELDS value") != 0)
 		return -1;
 	uint32_t map_sz = mp_decode_map(data);
 	for (uint32_t i = 0; i < map_sz; ++i) {
 		uint32_t svp = region_used(region);
-		const char *key = mp_decode_and_copy_str(data, region);
-		if (key == NULL)
+		if (CHECK_MP_TYPE(data, MP_STR, "error payload field name"))
 			return -1;
+		const char *key = mp_decode_and_copy_str(data, region);
 		const char *value = *data;
 		mp_next(data);
 		uint32_t value_len = *data - value;
@@ -318,44 +326,48 @@ mp_decode_error_one(const char **data)
 	struct error *err = NULL;
 	uint32_t map_size;
 
-	if (mp_typeof(**data) != MP_MAP)
-		goto error;
-
+	if (CHECK_MP_TYPE(data, MP_MAP, "MP_ERROR_STACK array entry") != 0)
+		goto finish;
 	map_size = mp_decode_map(data);
 	for (uint32_t i = 0; i < map_size; ++i) {
-		if (mp_typeof(**data) != MP_UINT)
-			goto error;
-
+		if (CHECK_MP_TYPE(data, MP_UINT, "error field key") != 0)
+			goto finish;
 		uint64_t key = mp_decode_uint(data);
 		switch(key) {
 		case MP_ERROR_TYPE:
-			mp_err.type = mp_decode_and_copy_str(data, region);
-			if (mp_err.type == NULL)
+			if (CHECK_MP_TYPE(data, MP_STR,
+					  "MP_ERROR_TYPE value") != 0)
 				goto finish;
+			mp_err.type = mp_decode_and_copy_str(data, region);
 			break;
 		case MP_ERROR_FILE:
-			mp_err.file = mp_decode_and_copy_str(data, region);
-			if (mp_err.file == NULL)
+			if (CHECK_MP_TYPE(data, MP_STR,
+					  "MP_ERROR_FILE value") != 0)
 				goto finish;
+			mp_err.file = mp_decode_and_copy_str(data, region);
 			break;
 		case MP_ERROR_LINE:
-			if (mp_typeof(**data) != MP_UINT)
-				goto error;
+			if (CHECK_MP_TYPE(data, MP_UINT,
+					  "MP_ERROR_LINE value") != 0)
+				goto finish;
 			mp_err.line = mp_decode_uint(data);
 			break;
 		case MP_ERROR_MESSAGE:
-			mp_err.message = mp_decode_and_copy_str(data, region);
-			if (mp_err.message == NULL)
+			if (CHECK_MP_TYPE(data, MP_STR,
+					  "MP_ERROR_MESSAGE value") != 0)
 				goto finish;
+			mp_err.message = mp_decode_and_copy_str(data, region);
 			break;
 		case MP_ERROR_ERRNO:
-			if (mp_typeof(**data) != MP_UINT)
-				goto error;
+			if (CHECK_MP_TYPE(data, MP_UINT,
+					  "MP_ERROR_ERRNO value") != 0)
+				goto finish;
 			mp_err.saved_errno = mp_decode_uint(data);
 			break;
 		case MP_ERROR_CODE:
-			if (mp_typeof(**data) != MP_UINT)
-				goto error;
+			if (CHECK_MP_TYPE(data, MP_UINT,
+					  "MP_ERROR_CODE value") != 0)
+				goto finish;
 			mp_err.code = mp_decode_uint(data);
 			break;
 		case MP_ERROR_FIELDS:
@@ -372,11 +384,6 @@ mp_decode_error_one(const char **data)
 	region_truncate(region, region_svp);
 	mp_error_destroy(&mp_err);
 	return err;
-
-error:
-	diag_set(ClientError, ER_INVALID_MSGPACK,
-		 "Invalid MP_ERROR MsgPack format");
-	goto finish;
 }
 
 /**
@@ -460,28 +467,22 @@ error_to_mpstream(const struct error *error, struct mpstream *stream)
 struct error *
 error_unpack_unsafe(const char **data)
 {
+	const char *begin = *data;
 	struct error *err = NULL;
+	uint32_t map_size;
 
-	if (mp_typeof(**data) != MP_MAP) {
-		diag_set(ClientError, ER_INVALID_MSGPACK,
-			 "Invalid MP_ERROR MsgPack format");
-		return NULL;
-	}
-	uint32_t map_size = mp_decode_map(data);
+	if (CHECK_MP_TYPE(data, MP_MAP, "error data") != 0)
+		goto error;
+	map_size = mp_decode_map(data);
 	for (uint32_t i = 0; i < map_size; ++i) {
-		if (mp_typeof(**data) != MP_UINT) {
-			diag_set(ClientError, ER_INVALID_MSGPACK,
-				 "Invalid MP_ERROR MsgPack format");
+		if (CHECK_MP_TYPE(data, MP_UINT, "error data key") != 0)
 			goto error;
-		}
 		uint64_t key = mp_decode_uint(data);
 		switch(key) {
 		case MP_ERROR_STACK: {
-			if (mp_typeof(**data) != MP_ARRAY) {
-				diag_set(ClientError, ER_INVALID_MSGPACK,
-					 "Invalid MP_ERROR MsgPack format");
+			if (CHECK_MP_TYPE(data, MP_ARRAY,
+					  "MP_ERROR_STACK value") != 0)
 				goto error;
-			}
 			uint32_t stack_sz = mp_decode_array(data);
 			struct error *effect = NULL;
 			for (uint32_t i = 0; i < stack_sz; i++) {
@@ -493,7 +494,6 @@ error_unpack_unsafe(const char **data)
 					effect = cur;
 					continue;
 				}
-
 				error_set_prev(effect, cur);
 				effect = cur;
 			}
@@ -503,6 +503,11 @@ error_unpack_unsafe(const char **data)
 			mp_next(data);
 		}
 	}
+	if (err == NULL) {
+		diag_set(ClientError, ER_INVALID_MSGPACK,
+			 "MP_ERROR_STACK is missing");
+		goto error;
+	}
 	return err;
 
 error:
@@ -511,6 +516,9 @@ error_unpack_unsafe(const char **data)
 		error_ref(err);
 		error_unref(err);
 	}
+	err = diag_last_error(diag_get());
+	assert(err != NULL);
+	error_set_uint(err, "offset", *data - begin);
 	return NULL;
 }
 
@@ -519,7 +527,7 @@ error_unpack(const char **data, uint32_t len)
 {
 	const char *end = *data + len;
 	const char *check = *data;
-	if (mp_check(&check, end) != 0 || check != end)
+	if (mp_check_exact(&check, end) != 0)
 		return NULL;
 	return error_unpack_unsafe(data);
 }
diff --git a/src/box/msgpack.c b/src/box/msgpack.c
index d15a6ab45a534bbf7ea50f556e11a1aace2ff36e..59ba761948dccd988f948c932459d5ccaef6386d 100644
--- a/src/box/msgpack.c
+++ b/src/box/msgpack.c
@@ -39,6 +39,11 @@
 #include "mp_interval.h"
 #include "mp_compression.h"
 
+#include "diag.h"
+#include "error.h"
+#include "errcode.h"
+#include "trivia/util.h"
+
 static int
 msgpack_fprint_ext(FILE *file, const char **data, int depth)
 {
@@ -95,25 +100,84 @@ msgpack_check_ext_data(int8_t type, const char *data, uint32_t len)
 {
 	switch (type) {
 	case MP_DECIMAL:
-		return mp_validate_decimal(data, len);
+		if (mp_validate_decimal(data, len) != 0) {
+			diag_set(ClientError, ER_INVALID_MSGPACK,
+				 "cannot unpack decimal");
+			return -1;
+		}
+		return 0;
 	case MP_UUID:
-		return mp_validate_uuid(data, len);
+		if (mp_validate_uuid(data, len) != 0) {
+			diag_set(ClientError, ER_INVALID_MSGPACK,
+				 "cannot unpack uuid");
+			return -1;
+		}
+		return 0;
 	case MP_DATETIME:
-		return mp_validate_datetime(data, len);
+		if (mp_validate_datetime(data, len) != 0) {
+			diag_set(ClientError, ER_INVALID_MSGPACK,
+				 "cannot unpack datetime");
+			return -1;
+		}
+		return 0;
 	case MP_ERROR:
-		return mp_validate_error(data, len);
+		if (mp_validate_error(data, len) != 0) {
+			/* The error is set by error_unpack(). */
+			diag_add(ClientError, ER_INVALID_MSGPACK,
+				 "cannot unpack error");
+			return -1;
+		}
+		return 0;
 	case MP_INTERVAL:
-		return mp_validate_interval(data, len);
+		if (mp_validate_interval(data, len) != 0) {
+			diag_set(ClientError, ER_INVALID_MSGPACK,
+				 "cannot unpack interval");
+			return -1;
+		}
+		return 0;
 	case MP_COMPRESSION:
 	default:
 		return mp_check_ext_data_default(type, data, len);
 	}
 }
 
+/** Our handler invoked on mp_check() error. */
+static void
+msgpack_check_on_error(const struct mp_check_error *mperr)
+{
+	struct error *err;
+	switch (mperr->type) {
+	case MP_CHECK_ERROR_TRUNC:
+		err = diag_set(ClientError, ER_INVALID_MSGPACK,
+			       "truncated input");
+		error_set_int(err, "trunc_count", mperr->trunc_count);
+		break;
+	case MP_CHECK_ERROR_ILL:
+		err = diag_set(ClientError, ER_INVALID_MSGPACK,
+			       "illegal code");
+		break;
+	case MP_CHECK_ERROR_EXT:
+		/* The error is set by msgpack_check_ext_data(). */
+		err = diag_add(ClientError, ER_INVALID_MSGPACK,
+			       "invalid extension");
+		error_set_int(err, "ext_type", mperr->ext_type);
+		error_set_uint(err, "ext_len", mperr->ext_len);
+		break;
+	case MP_CHECK_ERROR_JUNK:
+		err = diag_set(ClientError, ER_INVALID_MSGPACK,
+			       "junk after input");
+		break;
+	default:
+		unreachable();
+	}
+	error_set_uint(err, "offset", mperr->pos - mperr->data);
+}
+
 void
 msgpack_init(void)
 {
 	mp_fprint_ext = msgpack_fprint_ext;
 	mp_snprint_ext = msgpack_snprint_ext;
 	mp_check_ext_data = msgpack_check_ext_data;
+	mp_check_on_error = msgpack_check_on_error;
 }
diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c
index 5868c6a3a9d9b473e9094db614e7e2fbf32ca337..1191a3b85bf2aef09ddcbd62dc3a0e832b6f713f 100644
--- a/src/lua/msgpack.c
+++ b/src/lua/msgpack.c
@@ -581,7 +581,7 @@ lua_msgpack_decode_cdata(lua_State *L, bool check)
 		}
 		const char *p = data;
 		if (mp_check(&p, data + data_len) != 0)
-			return luaL_error(L, "msgpack.decode: invalid MsgPack");
+			return luaT_error(L);
 	}
 	struct luaL_serializer *cfg = luaL_checkserializer(L);
 	luamp_decode(L, cfg, &data);
@@ -604,7 +604,7 @@ lua_msgpack_decode_string(lua_State *L, bool check)
 	if (check) {
 		const char *p = data + offset;
 		if (mp_check(&p, data + data_len) != 0)
-			return luaL_error(L, "msgpack.decode: invalid MsgPack");
+			return luaT_error(L);
 	}
 	struct luaL_serializer *cfg = luaL_checkserializer(L);
 	const char *p = data + offset;
@@ -772,7 +772,7 @@ luamp_push_with_translation(struct lua_State *L, const char *data,
 	size_t data_len = data_end - data;
 	struct luamp_object *obj = luamp_new_object(L, data_len);
 	memcpy((char *)obj->data, data, data_len);
-	assert(mp_check(&data, data_end) == 0 && data == data_end);
+	assert(mp_check_exact(&data, data_end) == 0);
 	obj->translation = translation;
 }
 
@@ -828,10 +828,8 @@ lua_msgpack_object_from_raw(struct lua_State *L)
 	}
 	const char *p = data;
 	const char *data_end = data + data_len;
-	if (mp_check(&p, data_end) != 0 || p != data_end) {
-		return luaL_error(L, "msgpack.object_from_raw: "
-				  "invalid MsgPack");
-	}
+	if (mp_check_exact(&p, data_end) != 0)
+		return luaT_error(L);
 	struct luamp_object *obj = luamp_new_object(L, data_len);
 	memcpy((char *)obj->data, data, data_len);
 	obj->cfg = luaL_checkserializer(L);
diff --git a/test/app-luatest/msgpack_test.lua b/test/app-luatest/msgpack_test.lua
index 73d9e79e84fe7b650ba0a8ab6be0862ccf38dd7c..3ac6e44d2368ac1ee9e3c0ef7f273257032fe044 100644
--- a/test/app-luatest/msgpack_test.lua
+++ b/test/app-luatest/msgpack_test.lua
@@ -2,6 +2,7 @@ local buffer = require('buffer')
 local console = require('console')
 local msgpack = require('msgpack')
 local ffi = require('ffi')
+local fun = require('fun')
 local t = require('luatest')
 
 local g = t.group()
@@ -124,7 +125,7 @@ g.test_encode_decode_buffer = function()
 end
 
 g.test_invalid_msgpack = function()
-    local err = "msgpack.decode: invalid MsgPack"
+    local err = "Invalid MsgPack - truncated input"
 
     -- Invalid msgpack.
     local first_buffer = {1, 2, 3}
@@ -138,6 +139,7 @@ g.test_invalid_msgpack = function()
                                       buf.rpos, buf:size() - 1)
 
     -- 0xc1 cannot be used in a valid MsgPack.
+    err = "Invalid MsgPack - illegal code"
     t.assert_error_msg_content_equals(err, msgpack.decode, '\xc1')
     t.assert_error_msg_content_equals(err, msgpack.decode, '\x91\xc1')
     t.assert_error_msg_content_equals(err, msgpack.decode, '\x81\xff\xc1')
@@ -245,10 +247,10 @@ g.test_object_from_raw = function()
     -- invalid msgpack
     local s = msgpack.encode(o)
     t.assert_error_msg_content_equals(
-        "msgpack.object_from_raw: invalid MsgPack",
+        "Invalid MsgPack - truncated input",
         msgpack.object_from_raw, s:sub(1, -2))
     t.assert_error_msg_content_equals(
-        "msgpack.object_from_raw: invalid MsgPack",
+        "Invalid MsgPack - junk after input",
         msgpack.object_from_raw, s .. s)
 end
 
@@ -546,3 +548,262 @@ g.test_object_autocomplete = function()
     local r = tabcomplete('mp:')
     t.assert_equals(r, {'mp:', 'mp:get(', 'mp:decode(', 'mp:iterator('})
 end
+
+local g_error_details_params = {
+    decode = {
+        func = msgpack.decode,
+        exact = false,
+    },
+    decode_cdata = {
+        func = function(data)
+            return msgpack.decode(ffi.cast('char *', data), #data)
+        end,
+        exact = false,
+    },
+    object_from_raw = {
+        func = msgpack.object_from_raw,
+        exact = true,
+    },
+}
+
+local g_error_details = t.group('msgpack.error-details', t.helpers.matrix({
+    test = fun.totable(fun.map(function(k) return k end,
+                               g_error_details_params)),
+}))
+
+g_error_details.test_error_details = function(cg)
+    local params = g_error_details_params[cg.params.test]
+    local function check_error(data, expected)
+        local errmsg_prefix = 'Invalid MsgPack - '
+        local ok, err = pcall(params.func, data)
+        t.assert_not(ok)
+        local actual, prev
+        while err ~= nil do
+            local v = err:unpack()
+            v.prev = nil
+            v.trace = nil
+            v.base_type = nil
+            t.assert_equals(v.type, 'ClientError')
+            v.type = nil
+            t.assert_equals(v.code, box.error.INVALID_MSGPACK)
+            v.code = nil
+            if v.message:startswith(errmsg_prefix) then
+                v.message = v.message:sub(#errmsg_prefix + 1)
+            end
+            if prev == nil then
+                actual = v
+            else
+                prev.prev = v
+            end
+            prev = v
+            err = err.prev
+        end
+        t.assert_equals(actual, expected)
+    end
+    check_error('\xc1', {
+        message = 'illegal code', offset = 0,
+    })
+    check_error('', {
+        message = 'truncated input', offset = 0, trunc_count = 1,
+    })
+    check_error('\x94\xc0', {
+        message = 'truncated input', offset = 2, trunc_count = 3,
+    })
+    check_error('\x94\xda\x00', {
+        message = 'truncated input', offset = 1, trunc_count = 4,
+    })
+    if params.exact then
+        check_error('\xc0\xc0', {
+            message = 'junk after input', offset = 1,
+        })
+        check_error('\x92\x91\xc0\xc0\xc1', {
+            message = 'junk after input', offset = 4,
+        })
+    end
+    check_error('\xc7\x04\x01\xc0\xc0\xc0\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 1,
+        prev = {message = 'cannot unpack decimal'},
+    })
+    check_error('\xc7\x04\x02\xc0\xc0\xc0\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 2,
+        prev = {message = 'cannot unpack uuid'},
+    })
+    check_error('\xc7\x04\x04\xc0\xc0\xc0\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 4,
+        prev = {message = 'cannot unpack datetime'},
+    })
+    check_error('\xc7\x04\x06\xc0\xc0\xc0\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 6,
+        prev = {message = 'cannot unpack interval'},
+    })
+    check_error('\xc7\x02\x03\x91\xc1', {
+        message = 'invalid extension', offset = 3, ext_len = 2, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {message = 'illegal code', offset = 1},
+        },
+    })
+    check_error('\xc7\x02\x03\x92\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 2, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {message = 'truncated input', offset = 2, trunc_count = 1},
+        },
+    })
+    check_error('\xc7\x04\x03\x92\xc0\xc0\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {message = 'junk after input', offset = 3},
+        },
+    })
+    check_error('\xc7\x01\x03\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 1, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {message = 'error data must be MP_MAP', offset = 0},
+        },
+    })
+    check_error('\xc7\x03\x03\x81\xc0\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 3, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {message = 'error data key must be MP_UINT', offset = 1},
+        },
+    })
+    check_error('\xc7\x01\x03\x80', {
+        message = 'invalid extension', offset = 3, ext_len = 1, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {message = 'MP_ERROR_STACK is missing', offset = 1},
+        },
+    })
+    check_error('\xc7\x04\x03\x81\x00\x91\x80', {
+        message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {message = 'MP_ERROR_TYPE is missing', offset = 4},
+        },
+    })
+    check_error('\xc7\x06\x03\x81\x00\x91\x81\x00\xa0', {
+        message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {message = 'MP_ERROR_MESSAGE is missing', offset = 6},
+        },
+    })
+    check_error('\xc7\x08\x03\x81\x00\x91\x82\x00\xa0\x03\xa0', {
+        message = 'invalid extension', offset = 3, ext_len = 8, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {message = 'MP_ERROR_FILE is missing', offset = 8},
+        },
+    })
+    check_error('\xc7\x03\x03\x81\x00\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 3, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {
+                message = 'MP_ERROR_STACK value must be MP_ARRAY',
+                offset = 2,
+            },
+        },
+    })
+    check_error('\xc7\x04\x03\x81\x00\x91\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 4, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {
+                message = 'MP_ERROR_STACK array entry must be MP_MAP',
+                offset = 3,
+            },
+        },
+    })
+    check_error('\xc7\x06\x03\x81\x00\x91\x81\xc0\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {message = 'error field key must be MP_UINT', offset = 4},
+        },
+    })
+    check_error('\xc7\x06\x03\x81\x00\x91\x81\x00\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {
+                message = 'MP_ERROR_TYPE value must be MP_STR',
+                offset = 5,
+            },
+        },
+    })
+    check_error('\xc7\x06\x03\x81\x00\x91\x81\x01\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {
+                message = 'MP_ERROR_FILE value must be MP_STR',
+                offset = 5,
+            },
+        },
+    })
+    check_error('\xc7\x06\x03\x81\x00\x91\x81\x02\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {
+                message = 'MP_ERROR_LINE value must be MP_UINT',
+                offset = 5,
+            },
+        },
+    })
+    check_error('\xc7\x06\x03\x81\x00\x91\x81\x03\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {
+                message = 'MP_ERROR_MESSAGE value must be MP_STR',
+                offset = 5,
+            },
+        },
+    })
+    check_error('\xc7\x06\x03\x81\x00\x91\x81\x04\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {
+                message = 'MP_ERROR_ERRNO value must be MP_UINT',
+                offset = 5,
+            },
+        },
+    })
+    check_error('\xc7\x06\x03\x81\x00\x91\x81\x05\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {
+                message = 'MP_ERROR_CODE value must be MP_UINT',
+                offset = 5,
+            },
+        },
+    })
+    check_error('\xc7\x06\x03\x81\x00\x91\x81\x06\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 6, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {
+                message = 'MP_ERROR_FIELDS value must be MP_MAP',
+                offset = 5,
+            },
+        },
+    })
+    check_error('\xc7\x08\x03\x81\x00\x91\x81\x06\x81\xc0\xc0', {
+        message = 'invalid extension', offset = 3, ext_len = 8, ext_type = 3,
+        prev = {
+            message = 'cannot unpack error',
+            prev = {
+                message = 'error payload field name must be MP_STR',
+                offset = 6,
+            },
+        },
+    })
+end