diff --git a/src/box/lua/error.cc b/src/box/lua/error.cc
index ab4d141298bd3d066a1d6bd6fc7199d784f4bbae..566919e69d705fc4078e1425eebbf4f90ba30205 100644
--- a/src/box/lua/error.cc
+++ b/src/box/lua/error.cc
@@ -112,37 +112,14 @@ lbox_error_last(lua_State *L)
 	if (lua_gettop(L) >= 1)
 		luaL_error(L, "box.error.last(): bad arguments");
 
-	/* TODO: use struct error here */
-	Exception *e = (Exception *) box_error_last();
-
+	struct error *e = box_error_last();
 	if (e == NULL) {
 		lua_pushnil(L);
-	} else {
-		/*
-		 * TODO: use luaL_pusherror here, move type_foreach_method
-		 * to error_unpack() in Lua.
-		 */
-		lua_newtable(L);
+		return 1;
+	}
 
-		lua_pushstring(L, "type");
-		lua_pushstring(L, e->type->name);
-		lua_settable(L, -3);
-
-		type_foreach_method(e->type, method) {
-			if (method_invokable<const char *>(method, e)) {
-				const char *s = method_invoke<const char *>(method, e);
-				lua_pushstring(L, method->name);
-				lua_pushstring(L, s);
-				lua_settable(L, -3);
-			} else if (method_invokable<int>(method, e)) {
-				int code = method_invoke<int>(method, e);
-				lua_pushstring(L, method->name);
-				lua_pushinteger(L, code);
-				lua_settable(L, -3);
-			}
-		}
-       }
-       return 1;
+	luaL_pusherror(L, e);
+	return 1;
 }
 
 static int
diff --git a/src/exception.cc b/src/exception.cc
index db7eba8b1dc853ff13a586d7d90edeec44d3c79a..be94f575bc200451f8bee622ab2cc87e0455a650 100644
--- a/src/exception.cc
+++ b/src/exception.cc
@@ -35,6 +35,7 @@
 #include <errno.h>
 
 #include "fiber.h"
+#include "reflection.h"
 
 extern "C" {
 
@@ -58,6 +59,26 @@ exception_log(struct error *error)
 	e->log();
 }
 
+const char *
+exception_get_string(struct error *e, const struct method *method)
+{
+	/* A workaround for for vtable */
+	Exception *ex = (Exception *) e;
+	if (!method_invokable<const char *>(method, ex))
+		return NULL;
+	return method_invoke<const char *>(method, ex);
+}
+
+int
+exception_get_int(struct error *e, const struct method *method)
+{
+	/* A workaround for vtable  */
+	Exception *ex = (Exception *) e;
+	if (!method_invokable<int>(method, ex))
+		return 0;
+	return method_invoke<int>(method, ex);
+}
+
 } /* extern "C" */
 
 /** out_of_memory::size is zero-initialized by the linker. */
@@ -66,8 +87,6 @@ static OutOfMemory out_of_memory(__FILE__, __LINE__,
 
 static const struct method exception_methods[] = {
 	make_method(&type_Exception, "message", &Exception::get_errmsg),
-	make_method(&type_Exception, "file", &Exception::get_file),
-	make_method(&type_Exception, "line", &Exception::get_line),
 	make_method(&type_Exception, "log", &Exception::log),
 	METHODS_SENTINEL
 };
diff --git a/src/exception.h b/src/exception.h
index 35c6722d0a48877fa4f3837aee8230a5d3c38b7b..2889bf7998ebd5a7194b9eaad5cbc7b7963aeb23 100644
--- a/src/exception.h
+++ b/src/exception.h
@@ -137,4 +137,9 @@ exception_init();
 	throw tnt_error(__VA_ARGS__);					\
 } while (0)
 
+extern "C" const char *
+exception_get_string(struct error *e, const struct method *method);
+extern "C" int
+exception_get_int(struct error *e, const struct method *method);
+
 #endif /* TARANTOOL_EXCEPTION_H_INCLUDED */
diff --git a/src/ffisyms.cc b/src/ffisyms.cc
index b727a648f5e8f4113f3abbadec5099219779f3ec..4b75e9de5c56071f258643165c30b86ae7d965ff 100644
--- a/src/ffisyms.cc
+++ b/src/ffisyms.cc
@@ -53,6 +53,7 @@
 #include <lib/csv/csv.h>
 #include <lua/clock.h>
 #include "title.h"
+#include "exception.h"
 
 #include <openssl/err.h>
 #include <openssl/evp.h>
@@ -147,4 +148,6 @@ void *ffi_symbols[] = {
 	(void *) OpenSSL_add_all_digests,
 	(void *) OpenSSL_add_all_ciphers,
 	(void *) ERR_load_crypto_strings,
+	(void *) exception_get_string,
+	(void *) exception_get_int,
 };
diff --git a/src/lua/init.lua b/src/lua/init.lua
index 975d34a41af063f7b9cbe7dce5173204a514460a..9644bc3979968f25f6a5dcbf6a701689eed4e11e 100644
--- a/src/lua/init.lua
+++ b/src/lua/init.lua
@@ -6,6 +6,12 @@ struct type;
 struct method;
 struct error;
 
+enum ctype {
+    CTYPE_VOID = 0,
+    CTYPE_INT,
+    CTYPE_CONST_CHAR_PTR
+};
+
 struct type {
     const char *name;
     const struct type *parent;
@@ -33,11 +39,26 @@ struct error {
     char _errmsg[DIAG_ERRMSG_MAX];
 };
 
-/* TODO: remove these declarations */
-const struct error *
-box_error_last(void);
-const char *
-box_error_message(const struct error *);
+enum { METHOD_ARG_MAX = 8 };
+
+struct method {
+    const struct type *owner;
+    const char *name;
+    enum ctype rtype;
+    enum ctype atype[METHOD_ARG_MAX];
+    int nargs;
+    bool isconst;
+
+    union {
+        /* Add extra space to get proper struct size in C */
+        void *_spacer[2];
+    };
+};
+
+char *
+exception_get_string(struct error *e, const struct method *method);
+int
+exception_get_int(struct error *e, const struct method *method);
 
 double
 tarantool_uptime(void);
@@ -45,6 +66,44 @@ typedef int32_t pid_t;
 pid_t getpid(void);
 ]]
 
+local REFLECTION_CACHE = {}
+
+local function reflection_enumerate(err)
+    local key = tostring(err._type)
+    local result = REFLECTION_CACHE[key]
+    if result ~= nil then
+        return result
+    end
+    result = {}
+    -- See type_foreach_method() in reflection.h
+    local t = err._type
+    while t ~= nil do
+        local m = t.methods
+        while m.name ~= nil do
+            result[ffi.string(m.name)] = m
+            m = m + 1
+        end
+        t = t.parent
+    end
+    REFLECTION_CACHE[key] = result
+    return result
+end
+
+local function reflection_get(err, method)
+    if method.nargs ~= 0 then
+        return nil -- NYI
+    end
+    if method.rtype == ffi.C.CTYPE_INT then
+        return tonumber(ffi.C.exception_get_int(err, method))
+    elseif method.rtype == ffi.C.CTYPE_CONST_CHAR_PTR then
+        local str = ffi.C.exception_get_string(err, method)
+        if str == nil then
+            return nil
+        end
+        return ffi.string(str)
+    end
+end
+
 local function error_type(err)
     return ffi.string(err._type.name)
 end
@@ -62,7 +121,6 @@ local function error_trace(err)
     }
 end
 
--- TODO: Use reflection
 local error_fields = {
     ["type"]        = error_type;
     ["message"]     = error_message;
@@ -77,6 +135,12 @@ local function error_unpack(err)
     for key, getter in pairs(error_fields)  do
         result[key] = getter(err)
     end
+    for key, getter in pairs(reflection_enumerate(err)) do
+        local value = reflection_get(err, getter)
+        if value ~= nil then
+            result[key] = value
+        end
+    end
     return result
 end
 
@@ -87,6 +151,13 @@ local function error_raise(err)
     error(err)
 end
 
+local function error_match(err, ...)
+    if not ffi.istype('struct error', err) then
+        error("Usage: error:match()")
+    end
+    return string.match(error_message(err), ...)
+end
+
 local function error_serialize(err)
     -- Return an error message only in admin console to keep compatibility
     return error_message(err)
@@ -95,6 +166,7 @@ end
 local error_methods = {
     ["unpack"] = error_unpack;
     ["raise"] = error_raise;
+    ["match"] = error_match; -- Tarantool 1.6 backward compatibility
     ["__serialize"] = error_serialize;
 }
 
@@ -103,6 +175,13 @@ local function error_index(err, key)
     if getter ~= nil then
         return getter(err)
     end
+    getter = reflection_enumerate(err)[key]
+    if getter ~= nil and getter.nargs == 0 then
+        local val = reflection_get(err, getter)
+        if val ~= nil then
+            return val
+        end
+    end
     return error_methods[key]
 end
 
@@ -113,26 +192,6 @@ local error_mt = {
 
 ffi.metatype('struct error', error_mt);
 
--- Override pcall to support Tarantool exceptions
-local pcall_lua = pcall
-
-local function pcall_wrap(status, ...)
-    if status == true then
-        return true, ...
-    end
-    if ffi.istype('struct error', (...)) then
-        -- Return the Tarantool error as string to keep compatibility.
-        -- Caller should check box.error.last() to get additional information.
-        return false, tostring((...))
-    elseif ... == 'C++ exception' then
-        return false, ffi.string(ffi.C.box_error_message(ffi.C.box_error_last()))
-    end
-    return status, ...
-end
-pcall = function(fun, ...)
-    return pcall_wrap(pcall_lua(fun, ...))
-end
-
 dostring = function(s, ...)
     local chunk, message = loadstring(s)
     if chunk == nil then
diff --git a/src/lua/utils.c b/src/lua/utils.c
index 0c316f301033f30f06e00a420fa388675c8462fc..69ee5cd80a92e50808f25c51b7fc7c0be14cb805 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -900,7 +900,7 @@ luaL_error_gc(struct lua_State *L)
 	return 0;
 }
 
-static void
+void
 luaL_pusherror(struct lua_State *L, struct error *e)
 {
 	assert(CTID_CONST_STRUCT_ERROR_REF != 0);
diff --git a/src/lua/utils.h b/src/lua/utils.h
index b3967f9cfb8ef59268fc6055b020df889c2f2be8..fe78654471837f163b8d494a0adf3c47e065f785 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -497,6 +497,9 @@ lbox_call(lua_State *L, int nargs, int nreturns);
 int
 lbox_cpcall(lua_State *L, lua_CFunction func, void *ud);
 
+void
+luaL_pusherror(struct lua_State *L, struct error *e);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 
diff --git a/test/app-tap/pcall.result b/test/app-tap/pcall.result
index 3d1c5592957d4b0bb541596026de2291a6c0bb4e..e38f08b3542f61086ea3f303cfcb3a6861bccd26 100644
--- a/test/app-tap/pcall.result
+++ b/test/app-tap/pcall.result
@@ -5,5 +5,10 @@
 pcall inside xpcall:	true	pcall is ok
 pcall with Lua error():	false	some message
 pcall with box.error():	false	Illegal parameters, some message
+pcall with box.error(): typeof	ctype<const struct error &>
+pcall with box.error(): .type	ClientError
+pcall with box.error(): .code	1
+pcall with box.error(): .message	Illegal parameters, some message
+pcall with box.error(): .match()	some
 pcall with no return:	1
 pcall with multireturn:	true	1	2	3
diff --git a/test/app-tap/pcall.test.lua b/test/app-tap/pcall.test.lua
index ef3b51f0203f470762a7f37d22e0e820538b7ca6..343fb366fdcea948116a715b58cd722e680a9525 100755
--- a/test/app-tap/pcall.test.lua
+++ b/test/app-tap/pcall.test.lua
@@ -1,5 +1,7 @@
 #!/usr/bin/env tarantool
 
+local ffi = require('ffi')
+
 print[[
 --------------------------------------------------------------------------------
 -- #267: Bad exception catching
@@ -32,6 +34,12 @@ local status, msg = pcall(function()
     box.error(box.error.ILLEGAL_PARAMS, 'some message')
 end)
 print('pcall with box.error():', status, msg)
+print('pcall with box.error(): typeof', ffi.typeof(msg))
+print('pcall with box.error(): .type', msg.type)
+print('pcall with box.error(): .code', msg.code)
+print('pcall with box.error(): .message', msg.message)
+-- Tarantool 1.6 backward compatibility
+print('pcall with box.error(): .match()', msg:match('some'))
 
 print('pcall with no return:', select('#', pcall(function() end)))
 print('pcall with multireturn:', pcall(function() return 1, 2, 3 end))
diff --git a/test/box/misc.result b/test/box/misc.result
index 50a2a2d147e60aed246543312b55ea5b5cdd25d9..4121b27f632b717a1000b2ad8d82857283056297 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -109,13 +109,40 @@ box.error.raise()
 ---
 - error: Illegal parameters, bla bla
 ...
-box.error.last()
+e
+---
+- error: '[string "return e "]:1: variable ''e'' is not declared'
+...
+e = box.error.last()
+---
+...
+e:unpack()
 ---
-- line: -1
+- type: ClientError
   code: 1
-  type: ClientError
   message: Illegal parameters, bla bla
-  file: '[C]'
+  trace:
+  - file: '[C]'
+    line: 4294967295
+...
+e.type
+---
+- ClientError
+...
+e.code
+---
+- 1
+...
+e.message
+---
+- Illegal parameters, bla bla
+...
+tostring(e)
+---
+- Illegal parameters, bla bla
+...
+e = nil
+---
 ...
 box.error.clear()
 ---
diff --git a/test/box/misc.test.lua b/test/box/misc.test.lua
index 1c6c3aa983cdd2728bde1b1714282a522f6c740b..bd9bc98b7537d1f0a5c15d6a916cac99e1fad01b 100644
--- a/test/box/misc.test.lua
+++ b/test/box/misc.test.lua
@@ -34,11 +34,17 @@ box.error({code = 123, reason = 'test'})
 box.error(box.error.ILLEGAL_PARAMS, "bla bla")
 box.error()
 box.error.raise()
-box.error.last()
+e
+e = box.error.last()
+e:unpack()
+e.type
+e.code
+e.message
+tostring(e)
+e = nil
 box.error.clear()
 box.error.last()
 box.error.raise()
-
 space = box.space.tweedledum
 
 ----------------