diff --git a/include/errcode.h b/include/errcode.h
index 5fa333d236e8c509ac08d43385b12de521472a8d..5d9248d41b75d400218f8806872d97391130cfd9 100644
--- a/include/errcode.h
+++ b/include/errcode.h
@@ -97,9 +97,9 @@ enum { TNT_ERRMSG_MAX = 512 };
 	/* 42 */_(ER_SPLICE,			2, "Field SPLICE error: %s") \
 	/* 43 */_(ER_TUPLE_IS_TOO_LONG,		2, "Tuple is too long %u") \
 	/* 44 */_(ER_UNKNOWN_UPDATE_OP,		2, "Unknown UPDATE operation") \
-	/* 45 */_(ER_EXACT_MATCH,		2, "Partial key in an exact match (key field count: %u, expected: %u)") \
+	/* 45 */_(ER_EXACT_MATCH,		2, "Invalid key part count in an exact match (expected %u, got %u)") \
 	/* 46 */_(ER_UNUSED46,			2, "Unused46") \
-	/* 47 */_(ER_KEY_PART_COUNT,		2, "Key part count %u is greater than index part count %u") \
+	/* 47 */_(ER_KEY_PART_COUNT,		2, "Invalid key part count (expected [0..%u], got %u)") \
 	/* 48 */_(ER_PROC_RET,			2, "Return type '%s' is not supported in the binary protocol") \
 	/* 49 */_(ER_TUPLE_NOT_FOUND,		2, "Tuple doesn't exist in index %u") \
 	/* 50 */_(ER_NO_SUCH_PROC,		2, "Procedure '%.*s' is not defined") \
diff --git a/src/box/bitset_index.cc b/src/box/bitset_index.cc
index 49d9ac00aed18f6e08a3f722796d361e77918132..a527b0ba63840ed5628aa6ec5c93236531dcb1d8 100644
--- a/src/box/bitset_index.cc
+++ b/src/box/bitset_index.cc
@@ -38,10 +38,6 @@
 #include "pickle.h"
 #include <lib/bitset/index.h>
 
-static struct index_traits bitset_index_traits = {
-	/* .allows_partial_key = */ false,
-};
-
 static inline size_t
 tuple_to_value(struct tuple *tuple)
 {
@@ -239,14 +235,16 @@ BitsetIndex::initIterator(struct iterator *iterator, enum iterator_type type,
 			  const char *key, u32 part_count) const
 {
 	assert(iterator->free == bitset_index_iterator_free);
+	assert ( (part_count == 1 && key != NULL) ||
+		 (part_count == 0 && key == NULL));
+	(void) part_count;
+
 	struct bitset_index_iterator *it = bitset_index_iterator(iterator);
 
 	const void *bitset_key = NULL;
 	size_t bitset_key_size = 0;
 
 	if (type != ITER_ALL) {
-		check_key_parts(key_def, part_count,
-				bitset_index_traits.allows_partial_key);
 		const char *key2 = key;
 		bitset_key_size = (size_t) load_varint32(&key2);
 		bitset_key = key2;
diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc
index 551fcdafab1757cc0e34b6567744b7c3b23bccad..d601debc66b6ff289de64ce20755335abc048f69 100644
--- a/src/box/box_lua.cc
+++ b/src/box/box_lua.cc
@@ -597,6 +597,7 @@ lbox_pushiterator(struct lua_State *L, Index *index,
 	holder->it = it;
 	memcpy(holder->key, key, size);
 
+	key_validate(index->key_def, type, (key ? holder->key : NULL), part_count);
 	index->initIterator(it, type, (key ? holder->key : NULL), part_count);
 }
 
@@ -924,6 +925,8 @@ lbox_index_count(struct lua_State *L)
 	u32 count = 0;
 	/* preparing index iterator */
 	struct iterator *it = index->position();
+
+	key_validate(index->key_def, ITER_EQ, key, key_part_count);
 	index->initIterator(it, ITER_EQ, key, key_part_count);
 	/* iterating over the index and counting tuples */
 	struct tuple *tuple;
diff --git a/src/box/hash_index.cc b/src/box/hash_index.cc
index 045e745d28b8d0af1af063ecfbc8d9fff8928b8b..1af823ad627f15d8e3715a80baa489caca677fd1 100644
--- a/src/box/hash_index.cc
+++ b/src/box/hash_index.cc
@@ -35,10 +35,6 @@
 #include "assoc.h"
 #include "errinj.h"
 
-static struct index_traits hash_index_traits = {
-	/* .allows_partial_key = */ false,
-};
-
 /* {{{ HashIndex Iterators ****************************************/
 
 struct hash_i32_iterator {
@@ -384,9 +380,10 @@ struct tuple *
 Hash32Index::findByKey(const char *key, u32 part_count) const
 {
 	assert(key_def->is_unique);
-	check_key_parts(key_def, part_count, false);
-
-	(void) part_count;
+	if (part_count != key_def->part_count) {
+		tnt_raise(ClientError, ER_EXACT_MATCH,
+			key_def->part_count, part_count);
+	}
 
 	struct tuple *ret = NULL;
 	struct mh_i32ptr_node_t node = int32_key_to_node(key);
@@ -467,15 +464,17 @@ void
 Hash32Index::initIterator(struct iterator *ptr, enum iterator_type type,
 			  const char *key, u32 part_count) const
 {
+	assert ( (part_count == 1 && key != NULL) ||
+		 (part_count == 0 && key == NULL));
+	(void) part_count;
 	assert(ptr->free == hash_iterator_free);
+
 	struct hash_i32_iterator *it = (struct hash_i32_iterator *) ptr;
 	struct mh_i32ptr_node_t node;
 
 	switch (type) {
 	case ITER_GE:
 		if (key != NULL) {
-			check_key_parts(key_def, part_count,
-					hash_index_traits.allows_partial_key);
 			node = int32_key_to_node(key);
 			it->h_pos = mh_i32ptr_get(int_hash, &node, NULL);
 			it->base.next = hash_iterator_i32_ge;
@@ -487,8 +486,6 @@ Hash32Index::initIterator(struct iterator *ptr, enum iterator_type type,
 		it->base.next = hash_iterator_i32_ge;
 		break;
 	case ITER_EQ:
-		check_key_parts(key_def, part_count,
-				hash_index_traits.allows_partial_key);
 		node = int32_key_to_node(key);
 		it->h_pos = mh_i32ptr_get(int_hash, &node, NULL);
 		it->base.next = hash_iterator_i32_eq;
@@ -559,7 +556,10 @@ struct tuple *
 Hash64Index::findByKey(const char *key, u32 part_count) const
 {
 	assert(key_def->is_unique);
-	check_key_parts(key_def, part_count, false);
+	if (part_count != key_def->part_count) {
+		tnt_raise(ClientError, ER_EXACT_MATCH,
+			key_def->part_count, part_count);
+	}
 
 	(void) part_count;
 
@@ -645,6 +645,9 @@ void
 Hash64Index::initIterator(struct iterator *ptr, enum iterator_type type,
 			  const char *key, u32 part_count) const
 {
+	assert ( (part_count == 1 && key != NULL) ||
+		 (part_count == 0 && key == NULL));
+	(void) part_count;
 	assert(ptr->free == hash_iterator_free);
 	struct hash_i64_iterator *it = (struct hash_i64_iterator *) ptr;
 	struct mh_i64ptr_node_t node;
@@ -652,8 +655,6 @@ Hash64Index::initIterator(struct iterator *ptr, enum iterator_type type,
 	switch (type) {
 	case ITER_GE:
 		if (key != NULL) {
-			check_key_parts(key_def, part_count,
-					hash_index_traits.allows_partial_key);
 			node = int64_key_to_node(key);
 			it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL);
 			it->base.next = hash_iterator_i64_ge;
@@ -665,8 +666,6 @@ Hash64Index::initIterator(struct iterator *ptr, enum iterator_type type,
 		it->base.next = hash_iterator_i64_ge;
 		break;
 	case ITER_EQ:
-		check_key_parts(key_def, part_count,
-			hash_index_traits.allows_partial_key);
 		node = int64_key_to_node(key);
 		it->h_pos = mh_i64ptr_get(int64_hash, &node, NULL);
 		it->base.next = hash_iterator_i64_eq;
@@ -731,7 +730,10 @@ struct tuple *
 HashStrIndex::findByKey(const char *key, u32 part_count) const
 {
 	assert(key_def->is_unique);
-	check_key_parts(key_def, part_count, false);
+	if (part_count != key_def->part_count) {
+		tnt_raise(ClientError, ER_EXACT_MATCH,
+			key_def->part_count, part_count);
+	}
 
 	struct tuple *ret = NULL;
 	const struct mh_lstrptr_node_t node = { key, NULL };
@@ -813,6 +815,8 @@ void
 HashStrIndex::initIterator(struct iterator *ptr, enum iterator_type type,
 			   const char *key, u32 part_count) const
 {
+	assert ( (part_count == 1 && key != NULL) ||
+		 (part_count == 0 && key == NULL));
 	(void) part_count;
 
 	assert(ptr->free == hash_iterator_free);
@@ -822,8 +826,6 @@ HashStrIndex::initIterator(struct iterator *ptr, enum iterator_type type,
 	switch (type) {
 	case ITER_GE:
 		if (key != NULL) {
-			check_key_parts(key_def, part_count,
-					hash_index_traits.allows_partial_key);
 			node.key = key;
 			it->h_pos = mh_lstrptr_get(str_hash, &node, NULL);
 			it->base.next = hash_iterator_lstr_ge;
@@ -835,8 +837,6 @@ HashStrIndex::initIterator(struct iterator *ptr, enum iterator_type type,
 		it->h_pos = mh_begin(str_hash);
 		break;
 	case ITER_EQ:
-		check_key_parts(key_def, part_count,
-				hash_index_traits.allows_partial_key);
 		node.key = key;
 		it->h_pos = mh_lstrptr_get(str_hash, &node, NULL);
 		it->base.next = hash_iterator_lstr_eq;
diff --git a/src/box/index.cc b/src/box/index.cc
index 01fcff75a998a231633f3ced17b5cf25174c92d9..091027036929e54adf69229b206db8b8f8461f5d 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -42,15 +42,40 @@ STRS(iterator_type, ITERATOR_TYPE);
 /* {{{ Utilities. **********************************************/
 
 void
-Index::check_key_parts(const struct key_def *key_def,
-		u32 part_count, bool partial_key_allowed)
+key_validate(struct key_def *key_def, enum iterator_type type, const char *key,
+	     uint32_t part_count)
 {
+	if (part_count == 0 && (type == ITER_ALL || key_def->type == TREE ||
+			 (key_def->type == HASH && type == ITER_GE))) {
+		assert(key == NULL);
+		return;
+	}
+
 	if (part_count > key_def->part_count)
 		tnt_raise(ClientError, ER_KEY_PART_COUNT,
-			  part_count, key_def->part_count);
-	if (!partial_key_allowed && part_count < key_def->part_count)
+			  key_def->part_count, part_count);
+
+	/* Check partial keys */
+	if ((key_def->type != TREE) && part_count < key_def->part_count) {
 		tnt_raise(ClientError, ER_EXACT_MATCH,
-			  part_count, key_def->part_count);
+			  key_def->part_count, part_count);
+	}
+
+	const char *key_data = key;
+	for (uint32_t part = 0; part < part_count; part++) {
+		uint32_t part_size = load_varint32(&key_data);
+
+		enum field_data_type part_type = key_def->parts[part].type;
+
+		if (part_type == NUM && part_size != sizeof(uint32_t))
+			tnt_raise(ClientError, ER_KEY_FIELD_TYPE, "u32");
+
+		if (part_type == NUM64 && part_size != sizeof(uint64_t) &&
+				part_size != sizeof(uint32_t))
+			tnt_raise(ClientError, ER_KEY_FIELD_TYPE, "u64");
+
+		key_data += part_size;
+	}
 }
 
 /**
diff --git a/src/box/index.h b/src/box/index.h
index a26b520ab88c291b38e4e9249c5f245d3c938c67..cb6f0414274a4a141c04c1bfaf6f953b3d6f499e 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -130,11 +130,16 @@ struct key_def {
 	enum index_type type;
 };
 
-/** Descriptor of index features. */
-struct index_traits
-{
-	bool allows_partial_key;
-};
+/**
+ * @brief Check that the key has correct part count and correct part size
+ * @param key_def key definition
+ * @param type iterator type (see enum iterator_type)
+ * @param key BER-encoded key
+ * @param part_count number of parts in \a key
+ */
+void
+key_validate(struct key_def *key_def, enum iterator_type type, const char *key,
+	     uint32_t part_count);
 
 /**
  * The manner in which replace in a unique index must treat
@@ -224,10 +229,6 @@ class Index: public Object {
 		return m_position;
 	}
 protected:
-	static void
-	check_key_parts(const struct key_def *key_def, u32 part_count,
-			bool partial_key_allowed);
-
 	static uint32_t
 	replace_check_dup(struct tuple *old_tuple,
 			  struct tuple *dup_tuple,
diff --git a/src/box/request.cc b/src/box/request.cc
index b5fa233c9bf1b6fb02acc1efe123e826c04d5006..802b01eded7c8776c39e7444195c8194d3bd2274 100644
--- a/src/box/request.cc
+++ b/src/box/request.cc
@@ -116,6 +116,7 @@ execute_update(struct request *request, struct txn *txn)
 
 	Index *pk = space_index(sp, 0);
 	/* Try to find the tuple by primary key. */
+	key_validate(pk->key_def, ITER_EQ, key, key_part_count);
 	struct tuple *old_tuple = pk->findByKey(key, key_part_count);
 
 	if (old_tuple == NULL)
@@ -163,6 +164,7 @@ execute_select(struct request *request, struct port *port)
 		const char *key = read_key(reqpos, reqend, &key_part_count);
 
 		struct iterator *it = index->position();
+		key_validate(index->key_def, ITER_EQ, key, key_part_count);
 		index->initIterator(it, ITER_EQ, key, key_part_count);
 
 		struct tuple *tuple;
@@ -199,6 +201,7 @@ execute_delete(struct request *request, struct txn *txn)
 	const char *key = read_key(reqpos, reqend, &key_part_count);
 	/* Try to find tuple by primary key */
 	Index *pk = space_index(sp, 0);
+	key_validate(pk->key_def, ITER_EQ, key, key_part_count);
 	struct tuple *old_tuple = pk->findByKey(key, key_part_count);
 
 	if (old_tuple == NULL)
diff --git a/src/box/tree_index.cc b/src/box/tree_index.cc
index a09c4052694f349a8f66d40b109a43820931d780..306fe36f44fc25802293e3b4853753bac0867d28 100644
--- a/src/box/tree_index.cc
+++ b/src/box/tree_index.cc
@@ -35,10 +35,6 @@
 
 /* {{{ Utilities. *************************************************/
 
-static struct index_traits tree_index_traits = {
-	/* .allows_partial_key = */ true,
-};
-
 /**
  * Unsigned 32-bit int comparison.
  */
@@ -1015,7 +1011,10 @@ struct tuple *
 TreeIndex::findByKey(const char *key, u32 part_count) const
 {
 	assert(key_def->is_unique);
-	check_key_parts(key_def, part_count, false);
+	if (part_count != key_def->part_count) {
+		tnt_raise(ClientError, ER_EXACT_MATCH,
+			key_def->part_count, part_count);
+	}
 
 	struct key_data *key_data = (struct key_data *)
 			alloca(sizeof(struct key_data) +
@@ -1103,12 +1102,11 @@ void
 TreeIndex::initIterator(struct iterator *iterator, enum iterator_type type,
 			const char *key, u32 part_count) const
 {
+	assert ( (part_count >= 1 && key != NULL) ||
+		 (part_count == 0 && key == NULL));
 	struct tree_iterator *it = tree_iterator(iterator);
 
-	if (part_count != 0) {
-		check_key_parts(key_def, part_count,
-				tree_index_traits.allows_partial_key);
-	} else {
+	if (part_count == 0) {
 		/*
 		 * If no key is specified, downgrade equality
 		 * iterators to a full range.
diff --git a/test/big/hash.result b/test/big/hash.result
index 56c1ba70d46f0fcd88c52e3dfd648c40b068759d..373f31d9efa471a43cea96e2807369ad5e28838d 100644
--- a/test/big/hash.result
+++ b/test/big/hash.result
@@ -100,7 +100,7 @@ error: 'Supplied key field type does not match index type: expected u32'
 ...
 lua box.space[10]:select(0, 1, 2)
 ---
-error: 'Key part count 2 is greater than index part count 1'
+error: 'Invalid key part count (expected [0..1], got 2)'
 ...
 
 #-----------------------------------------------------------------------------#
@@ -141,7 +141,7 @@ error: 'Supplied key field type does not match index type: expected u32'
 ...
 lua box.space[10]:delete(1, 2)
 ---
-error: 'Key part count 2 is greater than index part count 1'
+error: 'Invalid key part count (expected [0..1], got 2)'
 ...
 
 #=============================================================================#
@@ -245,7 +245,7 @@ error: 'Supplied key field type does not match index type: expected u64'
 ...
 lua box.space[11]:select(0, '00000001', '00000002')
 ---
-error: 'Key part count 2 is greater than index part count 1'
+error: 'Invalid key part count (expected [0..1], got 2)'
 ...
 
 #-----------------------------------------------------------------------------#
@@ -286,7 +286,7 @@ error: 'Supplied key field type does not match index type: expected u64'
 ...
 lua box.space[11]:delete('00000001', '00000002')
 ---
-error: 'Key part count 2 is greater than index part count 1'
+error: 'Invalid key part count (expected [0..1], got 2)'
 ...
 
 #=============================================================================#
@@ -372,7 +372,7 @@ lua box.space[12]:select(0, 'key 5')
 
 lua box.space[12]:select(0, 'key 1', 'key 2')
 ---
-error: 'Key part count 2 is greater than index part count 1'
+error: 'Invalid key part count (expected [0..1], got 2)'
 ...
 
 #-----------------------------------------------------------------------------#
@@ -409,7 +409,7 @@ lua box.space[12]:delete('key 5')
 
 lua box.space[12]:delete('key 1', 'key 2')
 ---
-error: 'Key part count 2 is greater than index part count 1'
+error: 'Invalid key part count (expected [0..1], got 2)'
 ...
 lua box.space[10]:truncate()
 ---
diff --git a/test/big/iterator.result b/test/big/iterator.result
index 460a83a06ee5191bb9a24ba63aae38aca04bbc3f..21609efd1f75cbdd77a41fc7e0513989b4c3f294 100644
--- a/test/big/iterator.result
+++ b/test/big/iterator.result
@@ -801,7 +801,7 @@ $pid_023$
 ...
 lua iterate(20, 4, 0, 1, box.index.EQ)
 ---
-error: 'Partial key in an exact match (key field count: 0, expected: 1)'
+error: 'Invalid key part count in an exact match (expected 1, got 0)'
 ...
 lua iterate(20, 4, 0, 1, box.index.GE)
 ---
diff --git a/test/big/lua.result b/test/big/lua.result
index d3ba846c9d65eaa17bae27235a79bfa824d0d686..5c680343cd5579a7371a65075d853fdbfbf80200 100644
--- a/test/big/lua.result
+++ b/test/big/lua.result
@@ -20,7 +20,7 @@ Found 1 tuple:
 # https://bugs.launchpad.net/tarantool/+bug/904208
 #
 call box.select(1, 1, 'new', 'world', 'order')
-An error occurred: ER_KEY_PART_COUNT, 'Key part count 3 is greater than index part count 2'
+An error occurred: ER_KEY_PART_COUNT, 'Invalid key part count (expected [0..2], got 3)'
 call box.delete(1, 'brave')
 Found 1 tuple:
 ['brave', 'new', 'world']
diff --git a/test/big/tree_pk_multipart.result b/test/big/tree_pk_multipart.result
index e57579bd7bce45688022962c57d71eb514f88e6d..f5d87a886b79d8a3f41ef7de36f1c9cd30d615b6 100644
--- a/test/big/tree_pk_multipart.result
+++ b/test/big/tree_pk_multipart.result
@@ -198,11 +198,11 @@ lua box.delete(9, 'Vincent', 'The Wolf!', 3)
 ...
 lua box.delete(9, 'Vincent', 'The Wolf!')
 ---
-error: 'Partial key in an exact match (key field count: 2, expected: 3)'
+error: 'Invalid key part count in an exact match (expected 3, got 2)'
 ...
 lua box.delete(9, 'The Wolf!', 'Vincent', 1, 'Come again?')
 ---
-error: 'Key part count 4 is greater than index part count 3'
+error: 'Invalid key part count (expected [0..3], got 4)'
 ...
 lua box.update(9, {'The Wolf!', 'Vincent', 1}, '=p', 3, '<ooops>')
 ---
@@ -227,11 +227,11 @@ lua box.update(9, {'Vincent', 'The Wolf!', 3}, '=p', 3, '<ooops>')
 ...
 lua box.update(9, {'Vincent', 'The Wolf!'}, '=p', 3, '<ooops>')
 ---
-error: 'Partial key in an exact match (key field count: 2, expected: 3)'
+error: 'Invalid key part count in an exact match (expected 3, got 2)'
 ...
 lua box.update(9, {'The Wolf!', 'Vincent', 1, 'Come again?'}, '=p', 3, '<ooops>')
 ---
-error: 'Key part count 4 is greater than index part count 3'
+error: 'Invalid key part count (expected [0..3], got 4)'
 ...
 lua box.space[9]:len()
 ---
diff --git a/test/box/lua.result b/test/box/lua.result
index bcaca2c74ff89aea4f052734a0fbb7ca58666b3d..0e33e52533f10507a6c19466f165b38847cea148 100644
--- a/test/box/lua.result
+++ b/test/box/lua.result
@@ -284,7 +284,7 @@ lua box.select(0, 0, 'abcd')
 ...
 lua box.select(0, 0)
 ---
-error: 'Partial key in an exact match (key field count: 0, expected: 1)'
+error: 'Invalid key part count in an exact match (expected 1, got 0)'
 ...
 lua box.select(0, 1)
 ---
@@ -292,7 +292,7 @@ error: 'No index #1 is defined in space 0'
 ...
 lua box.select(0)
 ---
-error: 'Partial key in an exact match (key field count: 0, expected: 1)'
+error: 'Invalid key part count in an exact match (expected 1, got 0)'
 ...
 call box.replace(0, 'abcd', 'hello', 'world')
 Found 1 tuple: