diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c
index 2126988ebe6dbefed8397f6fd6fe41925de59c50..3229e01d4b43bd6629c04e14a8201fc1538deddd 100644
--- a/src/lua/msgpack.c
+++ b/src/lua/msgpack.c
@@ -139,6 +139,10 @@ luamp_encode_r(struct lua_State *L, struct luaL_serializer *cfg,
 	case MP_MAP:
 		/* Map */
 		if (level >= cfg->encode_max_depth) {
+			if (! cfg->encode_deep_as_nil) {
+				return luaL_error(L, "Too high nest level - %d",
+						  level + 1);
+			}
 			mpstream_encode_nil(stream); /* Limit nested maps */
 			return MP_NIL;
 		}
@@ -160,6 +164,10 @@ luamp_encode_r(struct lua_State *L, struct luaL_serializer *cfg,
 	case MP_ARRAY:
 		/* Array */
 		if (level >= cfg->encode_max_depth) {
+			if (! cfg->encode_deep_as_nil) {
+				return luaL_error(L, "Too high nest level - %d",
+						  level + 1);
+			}
 			mpstream_encode_nil(stream); /* Limit nested arrays */
 			return MP_NIL;
 		}
diff --git a/src/lua/msgpackffi.lua b/src/lua/msgpackffi.lua
index 75fcc5ba01fd32209f36c36d97b1ed4bceede2e8..63d9b33b7c05604331ec8cbe6f8690881ed076ea 100644
--- a/src/lua/msgpackffi.lua
+++ b/src/lua/msgpackffi.lua
@@ -206,6 +206,10 @@ local function encode_r(buf, obj, level)
         encode_str(buf, obj)
     elseif type(obj) == "table" then
         if level >= msgpack.cfg.encode_max_depth then
+            if not msgpack.cfg.encode_deep_as_nil then
+                error(string.format('Too high nest level - %d',
+                                    msgpack.cfg.encode_max_depth + 1))
+            end
             encode_nil(buf)
             return
         end
diff --git a/src/lua/utils.c b/src/lua/utils.c
index 48401042e8c0eefe55a08faf87c2b5ceb51e1c93..6b545b816359669e77d2b5493ae24f3046f9fd27 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -235,6 +235,7 @@ static struct {
 	OPTION(LUA_TNUMBER,  encode_sparse_ratio, 2),
 	OPTION(LUA_TNUMBER,  encode_sparse_safe, 10),
 	OPTION(LUA_TNUMBER,  encode_max_depth, 32),
+	OPTION(LUA_TBOOLEAN, encode_deep_as_nil, 0),
 	OPTION(LUA_TBOOLEAN, encode_invalid_numbers, 1),
 	OPTION(LUA_TNUMBER,  encode_number_precision, 14),
 	OPTION(LUA_TBOOLEAN, encode_load_metatables, 1),
diff --git a/src/lua/utils.h b/src/lua/utils.h
index 2485e109060640ddfbcc00d3e239c2e5242c8a4c..8f8630f8dcdd519e8f90d4c05ecc5674e4608c70 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -210,6 +210,13 @@ struct luaL_serializer {
 	int encode_sparse_safe;
 	/** Max recursion depth for encoding (MsgPack, CJSON only) */
 	int encode_max_depth;
+	/**
+	 * A flag whether a table with too high nest level should
+	 * be cropped. The not-encoded fields are replaced with
+	 * one null. If not set, too high nesting is considered an
+	 * error.
+	 */
+	int encode_deep_as_nil;
 	/** Enables encoding of NaN and Inf numbers */
 	int encode_invalid_numbers;
 	/** Floating point numbers precision (YAML, CJSON only) */
diff --git a/test/app-tap/json.test.lua b/test/app-tap/json.test.lua
index 0a89668665647fbfa21687bf5dad8c5775bbf1d9..cd210c85fcbd2cc8a965a1180299d1a21e1c3637 100755
--- a/test/app-tap/json.test.lua
+++ b/test/app-tap/json.test.lua
@@ -38,9 +38,10 @@ tap.test("json", function(test)
     --
     -- gh-2888: Check the possibility of using options in encode()/decode().
     --
+    local orig_encode_deep_as_nil = serializer.cfg.encode_deep_as_nil
     local orig_encode_max_depth = serializer.cfg.encode_max_depth
     local sub = {a = 1, { b = {c = 1, d = {e = 1}}}}
-    serializer.cfg({encode_max_depth = 1})
+    serializer.cfg({encode_max_depth = 1, encode_deep_as_nil = true})
     test:ok(serializer.encode(sub) == '{"1":null,"a":1}',
             'depth of encoding is 1 with .cfg')
     serializer.cfg({encode_max_depth = orig_encode_max_depth})
@@ -121,5 +122,6 @@ tap.test("json", function(test)
     rec4['b'] = rec4
     test:is(serializer.encode(rec4),
             '{"a":{"a":null,"b":null},"b":{"a":null,"b":null}}')
-    serializer.cfg({encode_max_depth = orig_encode_max_depth})
+    serializer.cfg({encode_max_depth = orig_encode_max_depth,
+                    encode_deep_as_nil = orig_encode_deep_as_nil})
 end)
diff --git a/test/app-tap/lua/serializer_test.lua b/test/app-tap/lua/serializer_test.lua
index bedbf95a58d9bfb2f254b6a65ac9a576746306df..ce655da95f50d4a8696879242cf1bd8ff830f455 100644
--- a/test/app-tap/lua/serializer_test.lua
+++ b/test/app-tap/lua/serializer_test.lua
@@ -390,7 +390,7 @@ local function test_ucdata(test, s)
 end
 
 local function test_depth(test, s)
-    test:plan(1)
+    test:plan(3)
     --
     -- gh-4434: serializer update should be reflected in Lua.
     --
@@ -399,6 +399,25 @@ local function test_depth(test, s)
     test:is(s.cfg.encode_max_depth, max_depth + 5,
             "cfg({<name> = value}) is reflected in cfg.<name>")
     s.cfg({encode_max_depth = max_depth})
+
+    --
+    -- gh-4434 (yes, the same issue): let users choose whether
+    -- they want to raise an error on tables with too high nest
+    -- level.
+    --
+    local deep_as_nil = s.cfg.encode_deep_as_nil
+    s.cfg({encode_deep_as_nil = false})
+
+    local t = nil
+    for i = 1, max_depth + 1 do t = {t} end
+    local ok, err = pcall(s.encode, t)
+    test:ok(not ok, "too deep encode depth")
+
+    s.cfg({encode_max_depth = max_depth + 1})
+    ok, err = pcall(s.encode, t)
+    test:ok(ok, "no throw in a corner case")
+
+    s.cfg({encode_deep_as_nil = deep_as_nil, encode_max_depth = max_depth})
 end
 
 return {
diff --git a/test/app-tap/msgpackffi.test.lua b/test/app-tap/msgpackffi.test.lua
index 7277a5b89fd6b30ab2d37702a9b20f143fbfc922..1059449640048fc54965721d95f29421fe1c0269 100755
--- a/test/app-tap/msgpackffi.test.lua
+++ b/test/app-tap/msgpackffi.test.lua
@@ -36,7 +36,7 @@ local function test_offsets(test, s)
 end
 
 local function test_other(test, s)
-    test:plan(23)
+    test:plan(24)
     local buf = string.char(0x93, 0x6e, 0xcb, 0x42, 0x2b, 0xed, 0x30, 0x47,
         0x6f, 0xff, 0xff, 0xac, 0x77, 0x6b, 0x61, 0x71, 0x66, 0x7a, 0x73,
         0x7a, 0x75, 0x71, 0x71, 0x78)
@@ -82,6 +82,8 @@ local function test_other(test, s)
         return level
     end
     local msgpack = require('msgpack')
+    local deep_as_nil = msgpack.cfg.encode_deep_as_nil
+    msgpack.cfg({encode_deep_as_nil = true})
     local max_depth = msgpack.cfg.encode_max_depth
     local result_depth = check_depth(max_depth + 5)
     test:is(result_depth, max_depth,
@@ -105,7 +107,12 @@ local function test_other(test, s)
     while t ~= nil do level = level + 1 t = t.key end
     test:is(level, max_depth + 5, "recursive map")
 
-    msgpack.cfg({encode_max_depth = max_depth})
+    msgpack.cfg({encode_deep_as_nil = false})
+    local ok = pcall(check_depth, max_depth + 6)
+    test:ok(not ok, "exception is thrown when crop is not allowed")
+
+    msgpack.cfg({encode_deep_as_nil = deep_as_nil,
+                 encode_max_depth = max_depth})
 end
 
 tap.test("msgpackffi", function(test)
diff --git a/test/box/tuple.result b/test/box/tuple.result
index dcbec0fa02d312fb908df7a0070b88a8d185028c..1082ae84e919445346f5852d47b77558326d6fd5 100644
--- a/test/box/tuple.result
+++ b/test/box/tuple.result
@@ -1258,6 +1258,12 @@ s2:drop()
 max_depth = msgpack.cfg.encode_max_depth
 ---
 ...
+deep_as_nil = msgpack.cfg.encode_deep_as_nil
+---
+...
+msgpack.cfg({encode_deep_as_nil = true})
+---
+...
 t = nil
 ---
 ...
@@ -1295,6 +1301,6 @@ level == max_depth + 5 or {level, max_depth}
 ---
 - true
 ...
-msgpack.cfg({encode_max_depth = max_depth})
+msgpack.cfg({encode_max_depth = max_depth, encode_deep_as_nil = deep_as_nil})
 ---
 ...
diff --git a/test/box/tuple.test.lua b/test/box/tuple.test.lua
index b2b834376b22d144bdbe5c80270eb62794471b8c..8dd15a0a1072b032f472b07459ab500ff7cade7d 100644
--- a/test/box/tuple.test.lua
+++ b/test/box/tuple.test.lua
@@ -431,6 +431,8 @@ s2:drop()
 -- gh-4434: tuple should use global msgpack serializer.
 --
 max_depth = msgpack.cfg.encode_max_depth
+deep_as_nil = msgpack.cfg.encode_deep_as_nil
+msgpack.cfg({encode_deep_as_nil = true})
 t = nil
 for i = 1, max_depth + 5 do t = {t} end
 tuple = box.tuple.new(t):totable()
@@ -446,4 +448,4 @@ while tuple ~= nil do level = level + 1 tuple = tuple[1] end
 -- serializer allows deeper tables.
 level == max_depth + 5 or {level, max_depth}
 
-msgpack.cfg({encode_max_depth = max_depth})
+msgpack.cfg({encode_max_depth = max_depth, encode_deep_as_nil = deep_as_nil})
diff --git a/third_party/lua-cjson/lua_cjson.c b/third_party/lua-cjson/lua_cjson.c
index be2416c6924bac512373b911cebf8fabaf80c60d..3c5bb3765c3201252201f4f656b90bf4ccd99cee 100644
--- a/third_party/lua-cjson/lua_cjson.c
+++ b/third_party/lua-cjson/lua_cjson.c
@@ -400,14 +400,20 @@ static void json_append_data(lua_State *l, struct luaL_serializer *cfg,
     json_append_nil(cfg, json);
     break;
     case MP_MAP:
-    if (current_depth >= cfg->encode_max_depth)
+    if (current_depth >= cfg->encode_max_depth) {
+        if (! cfg->encode_deep_as_nil)
+            luaL_error(l, "Too high nest level");
         return json_append_nil(cfg, json); /* Limit nested maps */
+    }
     json_append_object(l, cfg, current_depth + 1, json);
     return;
     case MP_ARRAY:
     /* Array */
-    if (current_depth >= cfg->encode_max_depth)
+    if (current_depth >= cfg->encode_max_depth) {
+        if (! cfg->encode_deep_as_nil)
+            luaL_error(l, "Too high nest level");
         return json_append_nil(cfg, json); /* Limit nested arrays */
+    }
     json_append_array(l, cfg, current_depth + 1, json, field.size);
     return;
     case MP_EXT: