diff --git a/TODO b/TODO
index 971b040e81154db17727f4e1e5d7a44949b84aba..d1553ff90f1965cf4eee674af0ed9af415433231 100644
--- a/TODO
+++ b/TODO
@@ -3,7 +3,6 @@ Next (in order of descending priority/precedence):
 * Finish the test suite for the base functionality
 * Improve debugging and error handling:
 	- Variables: dump variable identifiers in stead of storage indexes
-	- Improve argument errors
 * Make sure cmusieve can be replaced seamlessly with the new plugin.
 * Fix/Report issues listed in 'doc/rfc/RFC Controversy.txt'
 
diff --git a/src/lib-sieve/cmd-redirect.c b/src/lib-sieve/cmd-redirect.c
index 3adfee6c2dbbc07ca708bb53dc445ac6b4ad7812..5f6393bcc6925a97764607fa5d536e55af23ac6a 100644
--- a/src/lib-sieve/cmd-redirect.c
+++ b/src/lib-sieve/cmd-redirect.c
@@ -131,7 +131,7 @@ static bool cmd_redirect_validate
 			norm_address = sieve_address_normalize(address, &error);
 		
 			if ( norm_address == NULL ) {
-				sieve_command_validate_error(validator, cmd, 
+				sieve_argument_validate_error(validator, arg, 
 					"specified redirect address '%s' is invalid: %s",
 					str_sanitize(str_c(address),128), error);
 			} else {
diff --git a/src/lib-sieve/cmd-require.c b/src/lib-sieve/cmd-require.c
index f6d25a3a7529ef451145b54c852b69dfd5a0a4ce..bd7ae1bb9be0828c1f85273a636465913eb515ae 100644
--- a/src/lib-sieve/cmd-require.c
+++ b/src/lib-sieve/cmd-require.c
@@ -75,7 +75,7 @@ static bool cmd_require_validate
 		}
 	} else {
 		/* Something else */
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(validator, arg, 
 			"the require command accepts a single string or string list argument, "
 			"but %s was found", 
 			sieve_ast_argument_name(arg));
diff --git a/src/lib-sieve/ext-encoded-character.c b/src/lib-sieve/ext-encoded-character.c
index 85532885ef7507a3fd36db97218e5c03e46e58c8..2eb6a0552681dbab64285a0fa0b43171fbcdedc6 100644
--- a/src/lib-sieve/ext-encoded-character.c
+++ b/src/lib-sieve/ext-encoded-character.c
@@ -234,7 +234,7 @@ bool arg_encoded_string_validate
 					/* We now know that the substitution is valid */	
 
 					if ( error_hex != 0 ) {
-						sieve_command_validate_error(validator, cmd, 
+						sieve_argument_validate_error(validator, *arg, 
 							"invalid unicode character 0x%08x in encoded character substitution",
 							error_hex);
 						result = FALSE;
diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c
index 7abd09a80b943b70c200be18654f3b7008d86898..f4d59daf31ebf4fe12edeaa7c65033d92d49a0be 100644
--- a/src/lib-sieve/ext-envelope.c
+++ b/src/lib-sieve/ext-envelope.c
@@ -250,7 +250,7 @@ static bool tst_envelope_validate
 	if ( !sieve_ast_stringlist_map(&epart, (void *) &not_address, 
 		_envelope_part_is_supported) ) {		
 		
-		sieve_command_validate_error(validator, tst, 
+		sieve_argument_validate_error(validator, epart, 
 			"specified envelope part '%s' is not supported by the envelope test", 
 				str_sanitize(sieve_ast_strlist_strc(epart), 64));
 		return FALSE;
@@ -261,7 +261,7 @@ static bool tst_envelope_validate
 			sieve_command_find_argument(tst, &address_part_tag);
 
 		if ( addrp_arg != NULL ) {
-			sieve_command_validate_error(validator, tst,
+			sieve_argument_validate_error(validator, addrp_arg,
 				"address part ':%s' specified while non-address envelope part '%s' "
 				"is tested with the envelope test",
                 sieve_ast_argument_tag(addrp_arg), not_address->identifier);
diff --git a/src/lib-sieve/plugins/body/tst-body.c b/src/lib-sieve/plugins/body/tst-body.c
index 9b3c1ed579d1ead31257ea73ddee60116d7639dc..aad6b11ec1c8896ad33c658154c47169bbf56e2e 100644
--- a/src/lib-sieve/plugins/body/tst-body.c
+++ b/src/lib-sieve/plugins/body/tst-body.c
@@ -134,7 +134,7 @@ static bool tag_body_transform_validate
 	 *     / :text
 	 */
 	if ( (bool) cmd->data ) {
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(validator, *arg, 
 			"the :raw, :content and :text arguments for the body test are mutually "
 			"exclusive, but more than one was specified");
 		return FALSE;
diff --git a/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c b/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c
index bf45d62aee951754dfb0a1278748556b4c19b6e7..1196a850af1747903818d715212cebbf08c2bd30 100644
--- a/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c
+++ b/src/lib-sieve/plugins/imapflags/ext-imapflags-common.c
@@ -54,7 +54,7 @@ bool ext_imapflags_command_validate
 	if ( sieve_ast_argument_type(arg) != SAAT_STRING && 
 		sieve_ast_argument_type(arg) != SAAT_STRING_LIST ) 
 	{
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(validator, arg, 
 			"the %s %s expects either a string (variable name) or "
 			"a string-list (list of flags) as first argument, but %s was found", 
 			cmd->command->identifier, sieve_command_type_name(cmd->command),
@@ -70,14 +70,14 @@ bool ext_imapflags_command_validate
 		{
 			if ( cmd->command == &tst_hasflag ) {
 				if ( sieve_ast_argument_type(arg) != SAAT_STRING_LIST ) {
-					sieve_command_validate_error(validator, cmd, 
+					sieve_argument_validate_error(validator, arg, 
 						"if a second argument is specified for the hasflag, the first "
 						"must be a string-list (variable-list), but %s was found",
 						sieve_ast_argument_name(arg));
 					return FALSE;
 				}
 			} else {
-				sieve_command_validate_error(validator, cmd, 
+				sieve_argument_validate_error(validator, arg, 
 					"if a second argument is specified for the %s %s, the first "
 					"must be a string (variable name), but %s was found",
 					cmd->command->identifier, sieve_command_type_name(cmd->command), 
@@ -89,7 +89,7 @@ bool ext_imapflags_command_validate
 		/* Then, check whether the second argument is permitted */
 		
 		if ( !sieve_ext_variables_is_active(validator) )	{
-			sieve_command_validate_error(validator, cmd, 
+			sieve_argument_validate_error(validator,arg, 
 				"the %s %s only allows for the specification of a "
 				"variable name when the variables extension is active",
 				cmd->command->identifier, sieve_command_type_name(cmd->command));
@@ -103,7 +103,7 @@ bool ext_imapflags_command_validate
 		if ( sieve_ast_argument_type(arg2) != SAAT_STRING && 
 			sieve_ast_argument_type(arg2) != SAAT_STRING_LIST ) 
 		{
-			sieve_command_validate_error(validator, cmd, 
+			sieve_argument_validate_error(validator, arg2, 
 				"the %s %s expects a string list (list of flags) as "
 				"second argument when two arguments are specified, "
 				"but %s was found",
@@ -126,7 +126,7 @@ bool ext_imapflags_command_validate
 
 		while ( (flag=ext_imapflags_iter_get_flag(&fiter)) != NULL ) {
 			if ( !flag_is_valid(flag) ) {
-				sieve_command_validate_warning(validator, cmd,
+				sieve_argument_validate_warning(validator, arg,
                 	"IMAP flag '%s' specified for the %s command is invalid "
 					"and will be ignored (only first invalid is reported)",					
 					str_sanitize(flag, 64), cmd->command->identifier);
diff --git a/src/lib-sieve/plugins/include/cmd-import.c b/src/lib-sieve/plugins/include/cmd-import.c
index 9f9f75b548cbbf1707cf426dfb54cad305ae899c..bc1e9376c5f4afca628c6bda32f8d6c23b02a098 100644
--- a/src/lib-sieve/plugins/include/cmd-import.c
+++ b/src/lib-sieve/plugins/include/cmd-import.c
@@ -150,7 +150,7 @@ static bool cmd_import_validate
 		}
 	} else {
 		/* Something else */
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(validator, arg, 
 			"the %s command accepts a single string or string list argument, "
 			"but %s was found", cmd->command->identifier,
 			sieve_ast_argument_name(arg));
@@ -164,6 +164,7 @@ static bool cmd_import_validate
 			(prev_context->first_positional, cmd->first_positional);
 		
 		if ( prev_context->first_positional == NULL ) {
+			/* Not going to happen unless MAXINT stringlist items are specified */
 			sieve_command_validate_error(validator, cmd, 
 				"compiler reached AST limit (script too complex)");
 			return FALSE;
diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c
index e249ae6fb7d4f2ebc4dc7dbeeeb05353d5e7bb6a..ca1a329e2997953a3c80dcaf10ba2168114f27c7 100644
--- a/src/lib-sieve/plugins/include/cmd-include.c
+++ b/src/lib-sieve/plugins/include/cmd-include.c
@@ -113,7 +113,7 @@ static bool cmd_include_validate_location_tag
 		(struct cmd_include_context_data *) cmd->data;
 	
 	if ( ctx_data->location_assigned) {
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(validator, *arg, 
 			"include: cannot use location tags ':personal' and ':global' "
 			"multiple times");
 		return FALSE;
@@ -186,7 +186,7 @@ static bool cmd_include_validate(struct sieve_validator *validator,
 	script_name = sieve_ast_argument_strc(arg);
 
 	if ( strchr(script_name, '/') != NULL ) {
- 		sieve_command_validate_error(validator, cmd,
+ 		sieve_argument_validate_error(validator, arg,
 			"include: '/' not allowed in script name (%s)",
 			str_sanitize(script_name, 80));
 		return FALSE;
@@ -195,7 +195,7 @@ static bool cmd_include_validate(struct sieve_validator *validator,
 	script_dir = ext_include_get_script_directory
 		(ctx_data->location, script_name);
 	if ( script_dir == NULL ) {
-		sieve_command_validate_error(validator, cmd,
+		sieve_argument_validate_error(validator, arg,
 			"include: specified location for included script '%s' is unavailable "
 			"(system logs should provide more information)",
 			str_sanitize(script_name, 80));
diff --git a/src/lib-sieve/plugins/regex/mcht-regex.c b/src/lib-sieve/plugins/regex/mcht-regex.c
index cd4b41b44afcebc46278e158880fc96ee350d7eb..77536621db0044fe97433fbc315e1d0643be24a6 100644
--- a/src/lib-sieve/plugins/regex/mcht-regex.c
+++ b/src/lib-sieve/plugins/regex/mcht-regex.c
@@ -85,14 +85,15 @@ static const char *_regexp_error(regex_t *regexp, int errorcode)
 }
 
 static int mcht_regex_validate_regexp
-(struct sieve_validator *validator, struct sieve_match_type_context *ctx,
+(struct sieve_validator *validator, 
+	struct sieve_match_type_context *ctx ATTR_UNUSED,
 	struct sieve_ast_argument *key, int cflags) 
 {
 	int ret;
 	regex_t regexp;
 
 	if ( (ret=regcomp(&regexp, sieve_ast_argument_strc(key), cflags)) != 0 ) {
-		sieve_command_validate_error(validator, ctx->command_ctx,
+		sieve_argument_validate_error(validator, key,
 			"invalid regular expression for regex match: %s", 
 			_regexp_error(&regexp, ret));
 
@@ -116,7 +117,7 @@ static int mcht_regex_validate_key_argument
 	struct _regex_key_context *keyctx = (struct _regex_key_context *) context;
 
 	if ( !sieve_argument_is_string_literal(key) ) {
-		sieve_command_validate_error(keyctx->valdtr, keyctx->mctx->command_ctx,
+		sieve_argument_validate_error(keyctx->valdtr, key,
 			"this sieve implementation currently does not accept variable strings "
 			"as regular expression");
 		return FALSE;
@@ -141,7 +142,7 @@ bool mcht_regex_validate_context
 		else if ( cmp == &i_octet_comparator )
 			cflags =  REG_EXTENDED | REG_NOSUB;
 		else {
-			sieve_command_validate_error(validator, ctx->command_ctx, 
+			sieve_argument_validate_error(validator, ctx->match_type_arg, 
 				"regex match type only supports "
 				"i;octet and i;ascii-casemap comparators" );
 			return FALSE;	
diff --git a/src/lib-sieve/plugins/relational/ext-relational-common.c b/src/lib-sieve/plugins/relational/ext-relational-common.c
index 88c80a0db3c3003e215150c7f4ccfaebe1070ac6..92c6dc39697054afe4862271982459a5bf13361e 100644
--- a/src/lib-sieve/plugins/relational/ext-relational-common.c
+++ b/src/lib-sieve/plugins/relational/ext-relational-common.c
@@ -52,7 +52,7 @@ bool mcht_relational_validate
 	 
 	/* Did we get a string in the first place ? */ 
 	if ( (*arg)->type != SAAT_STRING ) {
-		sieve_command_validate_error(validator, ctx->command_ctx, 
+		sieve_argument_validate_error(validator, *arg, 
 			"the :%s match-type requires a constant string argument being "
 			"one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", "
 			"but %s was found", 
@@ -114,7 +114,7 @@ bool mcht_relational_validate
 	}
 	
 	if ( rel_match >= REL_MATCH_INVALID ) {
-		sieve_command_validate_error(validator, ctx->command_ctx, 
+		sieve_argument_validate_error(validator, *arg, 
 			"the :%s match-type requires a constant string argument being "
 			"one of \"gt\", \"ge\", \"lt\", \"le\", \"eq\" or \"ne\", "
 			"but \"%s\" was found", 
diff --git a/src/lib-sieve/plugins/vacation/cmd-vacation.c b/src/lib-sieve/plugins/vacation/cmd-vacation.c
index 3779dc4edfefa9aea877daf29efd7401a70f8fe6..57c3343ce73e6452f929c6e2232768ea6b920244 100644
--- a/src/lib-sieve/plugins/vacation/cmd-vacation.c
+++ b/src/lib-sieve/plugins/vacation/cmd-vacation.c
@@ -282,7 +282,7 @@ static bool cmd_vacation_validate_string_tag
 	 			result = sieve_address_validate(address, &error);
 	 
 				if ( !result ) {
-					sieve_command_validate_error(validator, cmd, 
+					sieve_argument_validate_error(validator, *arg, 
 						"specified :from address '%s' is invalid for vacation action: %s", 
 						str_sanitize(str_c(address), 128), error);
 				}
diff --git a/src/lib-sieve/plugins/variables/cmd-set.c b/src/lib-sieve/plugins/variables/cmd-set.c
index 084d4bc0596a9a3c54a4edd13ebd6eea2317e4a0..759f9d2c8935aa250c9dd84749c7e96a66c2095d 100644
--- a/src/lib-sieve/plugins/variables/cmd-set.c
+++ b/src/lib-sieve/plugins/variables/cmd-set.c
@@ -133,7 +133,7 @@ static bool tag_modifier_validate
 			array_idx(&sctx->modifiers, i);
 	
 		if ( (*smdf)->precedence == modf->precedence ) {
-			sieve_command_validate_error(validator, cmd, 
+			sieve_argument_validate_error(validator, *arg, 
 				"modifiers :%s and :%s specified for the set command conflict "
 				"having equal precedence", 
 				(*smdf)->object.identifier, modf->object.identifier);
diff --git a/src/lib-sieve/plugins/variables/ext-variables-arguments.c b/src/lib-sieve/plugins/variables/ext-variables-arguments.c
index 5ec04df633e7269652dbcc6d91c28446e34e8056..67ff4fbed4e9817125012de49fd2d7e238749bce 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-arguments.c
+++ b/src/lib-sieve/plugins/variables/ext-variables-arguments.c
@@ -25,20 +25,20 @@
  */
 
 static inline void _ext_variables_scope_size_error
-(struct sieve_validator *valdtr, struct sieve_command_context *cmd,
+(struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
 	const char *variable)
 {
-	sieve_command_validate_error(valdtr, cmd, 
+	sieve_argument_validate_error(valdtr, arg, 
 		"(implicit) declaration of new variable '%s' exceeds the limit "
 		"(max variables: %u)", variable, 
 		SIEVE_VARIABLES_MAX_SCOPE_SIZE);
 }
 
 static inline void _ext_variables_match_index_error
-(struct sieve_validator *valdtr, struct sieve_command_context *cmd,
+(struct sieve_validator *valdtr, struct sieve_ast_argument *arg,
 	unsigned int variable_index)
 {
-	sieve_command_validate_error(valdtr, cmd, 
+	sieve_argument_validate_error(valdtr, arg, 
 		"match value index %u out of range (max: %u)", variable_index, 
 		SIEVE_VARIABLES_MAX_MATCH_INDEX);
 }
@@ -78,7 +78,7 @@ static struct sieve_ast_argument *ext_variables_variable_argument_create
 }
 
 static bool _sieve_variable_argument_activate
-(struct sieve_validator *validator, struct sieve_command_context *cmd, 
+(struct sieve_validator *validator, struct sieve_command_context *cmd ATTR_UNUSED, 
 	struct sieve_ast_argument *arg, bool assignment)
 {
 	bool result = FALSE;
@@ -99,7 +99,7 @@ static bool _sieve_variable_argument_activate
 		/* Check whether name parsing succeeded */	
 		if ( nelements < 0 || varstr != varend ) {
 			/* Parse failed */
-			sieve_command_validate_error(validator, cmd, 
+			sieve_argument_validate_error(validator, arg, 
 				"invalid variable name '%s'", str_sanitize(str_c(variable),80));
 		} else if ( nelements == 1 ) {
 			/* Normal (match) variable */
@@ -114,7 +114,7 @@ static bool _sieve_variable_argument_activate
 
 				if ( var == NULL ) {
 					_ext_variables_scope_size_error
-						(validator, cmd, str_c(cur_element->identifier));
+						(validator, arg, str_c(cur_element->identifier));
 				} else {
 					arg->argument = &variable_argument;
 					arg->context = (void *) var;
@@ -126,7 +126,7 @@ static bool _sieve_variable_argument_activate
 				if ( !assignment ) {
 					if ( cur_element->num_variable > SIEVE_VARIABLES_MAX_MATCH_INDEX ) {
 						_ext_variables_match_index_error
-							(validator, cmd, cur_element->num_variable);
+							(validator, arg, cur_element->num_variable);
 					} else {
 						arg->argument = &match_value_argument;
 						arg->context = POINTER_CAST(cur_element->num_variable);
@@ -134,7 +134,7 @@ static bool _sieve_variable_argument_activate
 						result = TRUE;
 					}
 				} else {		
-					sieve_command_validate_error(validator, cmd, 
+					sieve_argument_validate_error(validator, arg, 
 						"cannot assign to match variable");
 				}
 			}
@@ -150,7 +150,7 @@ static bool _sieve_variable_argument_activate
 			 * the relevant extension MUST cause an error.
 			 */
 
-			sieve_command_validate_error(validator, cmd, 
+			sieve_argument_validate_error(validator, arg, 
 				"cannot %s to variable in unknown namespace '%s'", 
 				assignment ? "assign" : "refer", str_c(cur_element->identifier));
 		}
@@ -375,7 +375,7 @@ static bool arg_variable_string_validate
 								sieve_ast_arg_list_add(arglist, strarg);
 							else {
 								_ext_variables_scope_size_error
-									(validator, cmd, str_c(cur_element->identifier));
+									(validator, *arg, str_c(cur_element->identifier));
 								result = FALSE;
 								break;
 							}
@@ -383,7 +383,7 @@ static bool arg_variable_string_validate
 							/* Add match value argument '${000}' */
 							if ( cur_element->num_variable > SIEVE_VARIABLES_MAX_MATCH_INDEX ) {
 								_ext_variables_match_index_error
-									(validator, cmd, cur_element->num_variable);
+									(validator, *arg, cur_element->num_variable);
 								result = FALSE;
 								break;
 							}
@@ -403,7 +403,7 @@ static bool arg_variable_string_validate
 						/* References to namespaces without a prior require 
 						 * statement for thecrelevant extension MUST cause an error.
 					 	 */
-						sieve_command_validate_error(validator, cmd, 
+						sieve_argument_validate_error(validator, *arg, 
 							"referring to variable in unknown namespace '%s'", 
 							str_c(cur_element->identifier));
 						result = FALSE;
diff --git a/src/lib-sieve/sieve-commands.h b/src/lib-sieve/sieve-commands.h
index 28ec1ba32a3d1c27d5b90c27f0083056bcfb4a27..c3a112762927d521e8fd1877bbad1400aac64a8e 100644
--- a/src/lib-sieve/sieve-commands.h
+++ b/src/lib-sieve/sieve-commands.h
@@ -36,6 +36,13 @@ struct sieve_argument {
 #define sieve_argument_is_string_literal(arg) \
 	( (arg)->argument == &string_argument )
 
+/* Error handling */
+
+#define sieve_argument_validate_error(validator, arg_node, ...) \
+	sieve_validator_error(validator, (arg_node)->source_line, __VA_ARGS__)
+#define sieve_argument_validate_warning(validator, arg_node, ...) \
+	sieve_validator_warning(validator, (arg_node)->source_line, __VA_ARGS__)
+
 /* Literal arguments */
 
 extern const struct sieve_argument number_argument;
diff --git a/src/lib-sieve/sieve-comparators.c b/src/lib-sieve/sieve-comparators.c
index 706b8ca64bc3d17bb091d32b64c96e8a0a6501bf..038f858928ca63cecb5c8f860dd993a049fc7b99 100644
--- a/src/lib-sieve/sieve-comparators.c
+++ b/src/lib-sieve/sieve-comparators.c
@@ -152,7 +152,7 @@ static bool tag_comparator_validate
 	 *   ":comparator" <comparator-name: string>
 	 */
 	if ( (*arg)->type != SAAT_STRING ) {
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(validator, *arg, 
 			":comparator tag requires one string argument, but %s was found", 
 			sieve_ast_argument_name(*arg) );
 		return FALSE;
@@ -162,7 +162,7 @@ static bool tag_comparator_validate
 	cmp = sieve_comparator_find(validator, sieve_ast_argument_strc(*arg));
 	
 	if ( cmp == NULL ) {
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(validator, *arg, 
 			"unknown comparator '%s'", 
 			str_sanitize(sieve_ast_argument_strc(*arg),80));
 
diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c
index 23d8c65b3089ad596d5f39352689e6e4034845a0..f30c95c2e0c1386e5d8660c291a7fbc4d7f320da 100644
--- a/src/lib-sieve/sieve-match-types.c
+++ b/src/lib-sieve/sieve-match-types.c
@@ -366,6 +366,7 @@ static bool tag_match_type_is_instance_of
 	/* Create context */
 	mtctx = p_new(sieve_command_pool(cmd), struct sieve_match_type_context, 1);
 	mtctx->match_type = mtch;
+	mtctx->match_type_arg = arg;
 	mtctx->command_ctx = cmd;
 	mtctx->comparator = NULL; /* Can be filled in later */
 	
@@ -498,7 +499,7 @@ const struct sieve_operand match_type_operand = {
  */
 
 bool sieve_match_substring_validate_context
-(struct sieve_validator *validator, struct sieve_ast_argument *arg ATTR_UNUSED,
+(struct sieve_validator *validator, struct sieve_ast_argument *arg,
 	struct sieve_match_type_context *ctx,
 	struct sieve_ast_argument *key_arg ATTR_UNUSED)
 {
@@ -508,7 +509,7 @@ bool sieve_match_substring_validate_context
 		return TRUE;
 			
 	if ( (cmp->flags & SIEVE_COMPARATOR_FLAG_SUBSTRING_MATCH) == 0 ) {
-		sieve_command_validate_error(validator, ctx->command_ctx,
+		sieve_argument_validate_error(validator, arg,
 			"the specified %s comparator does not support "
 			"sub-string matching as required by the :%s match type",
 			cmp->object.identifier, ctx->match_type->object.identifier );
diff --git a/src/lib-sieve/sieve-match-types.h b/src/lib-sieve/sieve-match-types.h
index 775d67a6bce48de43a3e5602a26b4888342f42e5..a501952fe80d4218beb200f5ffb9549087d89553 100644
--- a/src/lib-sieve/sieve-match-types.h
+++ b/src/lib-sieve/sieve-match-types.h
@@ -75,6 +75,8 @@ struct sieve_match_type {
 
 struct sieve_match_type_context {
 	struct sieve_command_context *command_ctx;
+	struct sieve_ast_argument *match_type_arg;
+
 	const struct sieve_match_type *match_type;
 	
 	/* Only filled in when match_type->validate_context() is called */
diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c
index a9f913ba6f5671e6a7657cd373999d41aa0a0622..c8093812a206eee9a58c620c2c13390753ab14a5 100644
--- a/src/lib-sieve/sieve-validator.c
+++ b/src/lib-sieve/sieve-validator.c
@@ -675,7 +675,7 @@ bool sieve_validate_positional_argument
 		(sieve_ast_argument_type(arg) != SAAT_STRING || 
 			req_type != SAAT_STRING_LIST) ) 
 	{
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(validator, arg, 
 			"the %s %s expects %s as argument %d (%s), but %s was found", 
 			cmd->command->identifier, sieve_command_type_name(cmd->command), 
 			sieve_ast_argument_type_name(req_type),
@@ -695,7 +695,7 @@ bool sieve_validate_tag_parameter
 		(sieve_ast_argument_type(param) != SAAT_STRING || 
 			req_type != SAAT_STRING_LIST) ) 
 	{
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(validator, param, 
 			"the :%s tag for the %s %s requires %s as parameter, "
 			"but %s was found", sieve_ast_argument_tag(tag), 
 			cmd->command->identifier, sieve_command_type_name(cmd->command),
@@ -731,7 +731,7 @@ static bool sieve_validate_command_arguments
 			sieve_validator_find_tag(validator, cmd, arg, &id_code);
 		
 		if ( tag == NULL ) {
-			sieve_command_validate_error(validator, cmd, 
+			sieve_argument_validate_error(validator, arg, 
 				"unknown tagged argument ':%s' for the %s %s "
 				"(reported only once at first occurence)",
 				sieve_ast_argument_tag(arg), cmd->command->identifier, 
@@ -763,7 +763,7 @@ static bool sieve_validate_command_arguments
 					t_strdup_printf("%s argument (:%s)", tag->identifier, tag_id) : 
 					t_strdup_printf(":%s argument", tag->identifier); 	 
 				
-				sieve_command_validate_error(validator, cmd, 
+				sieve_argument_validate_error(validator, arg, 
 					"encountered duplicate %s for the %s %s",
 					tag_desc, cmd->command->identifier, 
 					sieve_command_type_name(cmd->command));
@@ -791,7 +791,7 @@ static bool sieve_validate_command_arguments
 	
 	while ( arg != NULL ) {
 		if ( sieve_ast_argument_type(arg) == SAAT_TAG ) {
-			sieve_command_validate_error(validator, cmd, 
+			sieve_argument_validate_error(validator, arg, 
 				"encountered an unexpected tagged argument ':%s' "
 				"while validating positional arguments for the %s %s",
 				sieve_ast_argument_tag(arg), cmd->command->identifier, 
@@ -865,7 +865,8 @@ static bool sieve_validate_command_subtests
 	case 0:
 	 	if ( sieve_ast_test_count(cmd->ast_node) > 0 ) {
 			sieve_command_validate_error(validator, cmd, 
-				"the %s %s accepts no sub-tests, but tests are specified anyway", 
+				"the %s %s accepts no sub-tests, but tests are specified anyway "
+				"(forgot semicolon?)", 
 				cmd->command->identifier, sieve_command_type_name(cmd->command));
 			
 			return FALSE;
diff --git a/src/lib-sieve/tst-address.c b/src/lib-sieve/tst-address.c
index 9a44da4300cf60e0e818b45ecbd78108ef5000d0..e6d01d0bbfb5426eae22e40102b70d5c2f3d142a 100644
--- a/src/lib-sieve/tst-address.c
+++ b/src/lib-sieve/tst-address.c
@@ -147,7 +147,7 @@ static bool tst_address_validate
 	 */
 	header = arg;
 	if ( !sieve_ast_stringlist_map(&header, NULL, _header_is_allowed) ) {		
-		sieve_command_validate_error(validator, tst, 
+		sieve_argument_validate_error(validator, header, 
 			"specified header '%s' is not allowed for the address test", 
 			str_sanitize(sieve_ast_strlist_strc(header), 64));
 		return FALSE;
diff --git a/src/lib-sieve/tst-size.c b/src/lib-sieve/tst-size.c
index 1a06fb61092b0a3ac239595ca55b340be7a1afcb..c4249429db11ab0b2380483012df5f11ae16bbb0 100644
--- a/src/lib-sieve/tst-size.c
+++ b/src/lib-sieve/tst-size.c
@@ -87,7 +87,7 @@ static bool tst_size_validate_over_tag
 	struct tst_size_context_data *ctx_data = (struct tst_size_context_data *) tst->data;	
 	
 	if ( ctx_data->type != SIZE_UNASSIGNED ) {
-		sieve_command_validate_error(validator, tst, TST_SIZE_ERROR_DUP_TAG);
+		sieve_argument_validate_error(validator, *arg, TST_SIZE_ERROR_DUP_TAG);
 		return FALSE;		
 	}
 	
@@ -106,7 +106,7 @@ static bool tst_size_validate_under_tag
 	struct tst_size_context_data *ctx_data = (struct tst_size_context_data *) tst->data;	
 	
 	if ( ctx_data->type != SIZE_UNASSIGNED ) {
-		sieve_command_validate_error(validator, tst, TST_SIZE_ERROR_DUP_TAG);
+		sieve_argument_validate_error(validator, *arg, TST_SIZE_ERROR_DUP_TAG);
 		return FALSE;		
 	}
 	
diff --git a/src/testsuite/testsuite-objects.c b/src/testsuite/testsuite-objects.c
index 3b4bd6cda5a4ba78c988f202350b4cfcfb6f39a0..3371c8815a2aeeaf6e41902bc1a7e5f3463f7a6f 100644
--- a/src/testsuite/testsuite-objects.c
+++ b/src/testsuite/testsuite-objects.c
@@ -207,7 +207,7 @@ bool testsuite_object_argument_activate
 	
 	object = testsuite_object_find(valdtr, objname);
 	if ( object == NULL ) {
-		sieve_command_validate_error(valdtr, cmd, 
+		sieve_argument_validate_error(valdtr, arg, 
 			"unknown testsuite object '%s'", objname);
 		return FALSE;
 	}
@@ -218,7 +218,7 @@ bool testsuite_object_argument_activate
 	if ( member != NULL ) {
 		if ( object->get_member_id == NULL || 
 			(member_id=object->get_member_id(member)) == -1 ) {
-			sieve_command_validate_error(valdtr, cmd, 
+			sieve_argument_validate_error(valdtr, arg, 
 				"member '%s' does not exist for testsuite object '%s'", member, objname);
 			return FALSE;
 		}