diff --git a/changelogs/unreleased/gh-6199-schema-priv-resolve-existence-check.md b/changelogs/unreleased/gh-6199-schema-priv-resolve-existence-check.md
new file mode 100644
index 0000000000000000000000000000000000000000..3390d312b07fac95f28d6f7e69fe65f9d7df40ff
--- /dev/null
+++ b/changelogs/unreleased/gh-6199-schema-priv-resolve-existence-check.md
@@ -0,0 +1,4 @@
+## bugfix/box
+
+* We now check that all privileges passed to box.schema.grant are resolved 
+  (gh-6199).
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index 99b65b0f51e30c3eeb53d3a6ef3442bcc83495f8..2c02949c507cb2b14d5fe46403fca0f467408f00 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -2380,53 +2380,56 @@ box.schema.sequence.drop = function(name, opts)
     _sequence:delete{id}
 end
 
-local function privilege_resolve(privilege)
-    local numeric = 0
-    if type(privilege) == 'string' then
-        privilege = string.lower(privilege)
-        if string.find(privilege, 'read') then
-            numeric = numeric + box.priv.R
-        end
-        if string.find(privilege, 'write') then
-            numeric = numeric + box.priv.W
-        end
-        if string.find(privilege, 'execute') then
-            numeric = numeric + box.priv.X
-        end
-        if string.find(privilege, 'session') then
-            numeric = numeric + box.priv.S
-        end
-        if string.find(privilege, 'usage') then
-            numeric = numeric + box.priv.U
-        end
-        if string.find(privilege, 'create') then
-            numeric = numeric + box.priv.C
-        end
-        if string.find(privilege, 'drop') then
-            numeric = numeric + box.priv.D
-        end
-        if string.find(privilege, 'alter') then
-            numeric = numeric + box.priv.A
-        end
-        if string.find(privilege, 'reference') then
-            numeric = numeric + box.priv.REFERENCE
-        end
-        if string.find(privilege, 'trigger') then
-            numeric = numeric + box.priv.TRIGGER
-        end
-        if string.find(privilege, 'insert') then
-            numeric = numeric + box.priv.INSERT
-        end
-        if string.find(privilege, 'update') then
-            numeric = numeric + box.priv.UPDATE
-        end
-        if string.find(privilege, 'delete') then
-            numeric = numeric + box.priv.DELETE
+local function privilege_parse(privs)
+    -- TODO: introduce a global privilege -> bit mapping?
+    local privs_map = {
+        read      = box.priv.R,
+        write     = box.priv.W,
+        execute   = box.priv.X,
+        session   = box.priv.S,
+        usage     = box.priv.U,
+        create    = box.priv.C,
+        drop      = box.priv.D,
+        alter     = box.priv.A,
+        reference = box.priv.REFERENECE,
+        trigger   = box.priv.TRIGGER,
+        insert    = box.priv.INSERT,
+        update    = box.priv.UPDATE,
+        delete    = box.priv.DELETE
+    }
+    local privs_cp = string.lower(privs):gsub('^[%A]*', '')
+
+    local mask = 0
+    -- TODO: prove correctness formally (e.g. via a FSA)?
+    repeat
+        local matched = false
+        -- TODO: replace this with one group pattern when Lua patterns start
+        -- supporting disjunction (e.g. '|')
+        for priv, bit in pairs(privs_map) do
+            privs_cp = string.gsub(privs_cp, '^' .. priv .. '[%A]*',
+                                   function()
+                                       matched = true
+                                       mask = mask + bit
+                                       privs_map[priv] = 0
+                                       return ''
+                                   end)
         end
-    else
-        numeric = privilege
+    until (not matched)
+
+    if privs_cp ~= '' then
+        mask = 0
+    end
+
+    return mask
+end
+
+local function privilege_resolve(privs)
+    if type(privs) == 'string' then
+        return privilege_parse(privs)
+    elseif type(privs) == 'number' then -- TODO: assert type(privs)?
+        return privs
     end
-    return numeric
+    return 0
 end
 
 -- allowed combination of privilege bits for object
diff --git a/test/box-luatest/gh_6199_schema_priv_resolve_existence_check_test.lua b/test/box-luatest/gh_6199_schema_priv_resolve_existence_check_test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..acd229f78f046e82dc12239d1cb0f745d31f6398
--- /dev/null
+++ b/test/box-luatest/gh_6199_schema_priv_resolve_existence_check_test.lua
@@ -0,0 +1,49 @@
+local cluster = require('test.luatest_helpers.cluster')
+local t = require('luatest')
+
+local g = t.group('gh-6199-schema-priv-resolve-existence-check')
+
+g.before_all(function()
+    local helpers = require('test.luatest_helpers')
+
+    g.cluster = cluster:new({})
+
+    local default_box_cfg = {
+        listen = helpers.instance_uri('default'),
+    }
+    g.default = g.cluster:build_and_add_server({alias   = 'default',
+                                                box_cfg = default_box_cfg})
+    g.cluster:start()
+    g.default:exec(function()
+        box.session.su('admin')
+    end)
+end)
+
+g.after_all(function()
+    g.cluster:drop()
+end)
+
+g.test_priv_resolve_existence_check = function()
+    g.default:exec(function()
+        local t = require('luatest')
+
+        local msg = 'role cannot be granted together with a privilege'
+        t.assert_error(box.schema.user.grant, 'guest', 'read,replication',
+                       'universe', msg)
+
+        msg = 'when privileges are resolved, we check that all of them have' ..
+              'been resolved'
+        local privs = {'read', 'write', 'execute', 'session', 'usage', 'read',
+                       'create', 'drop', 'alter', 'reference', 'trigger',
+                       'update', 'delete'}
+        for _, priv in pairs(privs) do
+            local invalid_priv = ('%s, unknown,%s'):format(priv, priv)
+            t.assert_error(box.schema.user.grant, 'guest', invalid_priv,
+                           'universe', msg)
+        end
+        local invalid_all_privs = table.concat(privs, ',') .. ',unknown' ..
+                                  table.concat(privs, ',')
+        t.assert_error(box.schema.user.grant, 'guest', invalid_all_privs,
+                       'universe', msg)
+    end)
+end