diff --git a/TODO b/TODO
index adf05021628b45df71a065930f06a77e4ef714ea..c5a0c9550e93fd5e2c1eb00aa0df5fb70eea8e87 100644
--- a/TODO
+++ b/TODO
@@ -1,8 +1,6 @@
 Next (in order of descending priority/precedence):
 * Full standards compliance review for the engine and all fully implemented 
   sieve extensions. Issues discovered so far:
-	- Encoded character processing must not raise an error on substitutions
-	  that are not syntactically valid.
 	- :matches : match values must only be changed when the test matches.
 	- If an address is not syntactically valid, then it will not be matched
 	  by tests specifying ":localpart" or ":domain".
diff --git a/src/lib-sieve/ext-encoded-character.c b/src/lib-sieve/ext-encoded-character.c
index 08a43e4c65f9acc11c1dd4fe83c58d04341f4adf..1097b3eeebbcd4bf7d417a2fa53284ce981299f2 100644
--- a/src/lib-sieve/ext-encoded-character.c
+++ b/src/lib-sieve/ext-encoded-character.c
@@ -118,12 +118,12 @@ static bool _parse_hexint
 	return ( digit > 0 );
 }
 
-static int _decode_hex
+static bool _decode_hex
 (const char **in, const char *inend, string_t *result) 
 {
 	int values = 0;
 	
-	for (;;) {
+	while ( *in < inend ) {
 		unsigned int hexpair;
 		
 		if ( !_skip_whitespace(in, inend) ) return FALSE;
@@ -134,19 +134,19 @@ static int _decode_hex
 		values++;
 	}
 	
-	return ( values > 0 ? 1 : 0 );
+	return ( values > 0 );
 }
 
 static int _decode_unicode
-(struct sieve_validator *validator, struct sieve_command_context *cmd, 
-	const char **in, const char *inend, string_t *result) 
+(const char **in, const char *inend, string_t *result, unsigned int *error_hex) 
 {
 	int values = 0;
+	bool valid = TRUE;
 	
-	for (;;) {
+	while ( *in < inend ) {
 		unsigned int unicode_hex;
 		
-		if ( !_skip_whitespace(in, inend) ) return 0;
+		if ( !_skip_whitespace(in, inend) ) return FALSE;
 		
 		if ( !_parse_hexint(in, inend, 0, &unicode_hex) ) break;
 
@@ -154,15 +154,13 @@ static int _decode_unicode
 			(unicode_hex >= 0xE000 && unicode_hex <= 0x10FFFF)	) 
 			uni_ucs4_to_utf8_c((unichar_t) unicode_hex, result);
 		else {
-			sieve_command_validate_error(validator, cmd, 
-				"invalid unicode character 0x%08x in encoded character substitution",
-					unicode_hex);
-			return -1;
+			if ( valid ) *error_hex = unicode_hex;
+			valid = FALSE;
 		}	
 		values++;
 	}
 	
-	return ( values > 0 ? 1 : 0 );
+	return ( values > 0 );
 }
 
 bool arg_encoded_string_validate
@@ -170,7 +168,6 @@ bool arg_encoded_string_validate
 		struct sieve_command_context *cmd)
 {
 	bool result = TRUE;
-	int ret;
 	enum { ST_NONE, ST_OPEN, ST_TYPE, ST_CLOSE } 
 		state = ST_NONE;
 	string_t *str = sieve_ast_argument_str(*arg);
@@ -178,6 +175,7 @@ bool arg_encoded_string_validate
 	const char *p, *mark, *strstart, *substart = NULL;
 	const char *strval = (const char *) str_data(str);
 	const char *strend = strval + str_len(str);
+	unsigned int error_hex = 0;
 
 	T_BEGIN {		
 		tmpstr = t_str_new(32);	
@@ -219,19 +217,13 @@ bool arg_encoded_string_validate
 				if ( strncasecmp(mark, "hex", p - mark) == 0 ) {
 					/* Hexadecimal */
 					p++;
-					ret = _decode_hex(&p, strend, tmpstr);
-					if ( ret <= 0 ) { 
+					if ( !_decode_hex(&p, strend, tmpstr) )
 						state = ST_NONE;
-						if ( ret < 0 ) result = FALSE;
-					}
 				} else if ( strncasecmp(mark, "unicode", p - mark) == 0 ) {
 					/* Unicode */
 					p++;
-					ret = _decode_unicode(validator, cmd, &p, strend, tmpstr);
-					if ( ret <= 0 ) { 
+					if ( !_decode_unicode(&p, strend, tmpstr, &error_hex) )
 						state = ST_NONE;
-						if ( ret < 0 ) result = FALSE;
-					}
 				} else {	
 					/* Invalid encoding */
 					p++;
@@ -241,6 +233,14 @@ bool arg_encoded_string_validate
 			case ST_CLOSE:
 				if ( *p == '}' ) {				
 					/* We now know that the substitution is valid */	
+
+					if ( error_hex != 0 ) {
+						sieve_command_validate_error(validator, cmd, 
+							"invalid unicode character 0x%08x in encoded character substitution",
+							error_hex);
+						result = FALSE;
+						break;
+					}
 					
 					if ( newstr == NULL ) {
 						newstr = str_new(sieve_ast_pool((*arg)->ast), str_len(str)*2);
diff --git a/src/testsuite/tests/compile/errors/encoded-character.sieve b/src/testsuite/tests/compile/errors/encoded-character.sieve
index 698eb0801b1bca84d0aa7f440d0a3a4a596e2e7d..04d9de40d4f788af5366878f65ae168e8a76963a 100644
--- a/src/testsuite/tests/compile/errors/encoded-character.sieve
+++ b/src/testsuite/tests/compile/errors/encoded-character.sieve
@@ -11,13 +11,13 @@ require "fileinto";
 fileinto "INBOX.${unicode:200000}";
 
 # Not an error
-#fileinto "INBOX.${unicode:200000";
+fileinto "INBOX.${unicode:200000";
 
 # Invalid unicode character (2)
 fileinto "INBOX.${Unicode:DF01}";
 
 # Not an error
-#fileinto "INBOX.${Unicode:DF01";
+fileinto "INBOX.${Unicode:DF01";
 
 
 
diff --git a/src/testsuite/tests/extensions/encoded-character.svtest b/src/testsuite/tests/extensions/encoded-character.svtest
index e241495ba29171833ffce9aaeefc61d401c83b3b..b05e2e4c6858091331ad717d6755c20919c37cab 100644
--- a/src/testsuite/tests/extensions/encoded-character.svtest
+++ b/src/testsuite/tests/extensions/encoded-character.svtest
@@ -83,6 +83,48 @@ test "HEX equality braindead" {
 	}
 }
 
+test "Syntax errors" {
+	if anyof( not string "$" "${hex:24}", not string "$ " "${hex:24} ", not string " $" " ${hex:24}" ) {
+        test_fail "loose $ handled inappropriately";
+    }
+
+	if anyof( not string "${" "${hex:24}{", not string "a${" "a${hex:24}{", not string "${a" "${hex:24}{a" ) {
+        test_fail "loose ${ handled inappropriately";
+    }
+
+	if anyof( not string "${}" "${hex:24}{}", not string "b${}" "b${hex:24}{}", not string "${}b" "${hex:24}{}b" ) {
+		test_fail "entirely missing content handled inappropriately";
+	}
+		
+	if not string "${:}" "${hex:24}{:}" {
+        test_fail "missing content handled inappropriately";
+    }
+	
+	if not string "${hex:}" "${hex:24}{hex:}" {
+        test_fail "missing hex content handled inappropriately";
+    }
+
+	if not string "${unicode:}" "${hex:24}{unicode:}" {
+        test_fail "missing unicode content handled inappropriately";
+    }
+
+	if not string "${hex:sss}" "${hex:24}{hex:sss}" {
+        test_fail "erroneous hex content handled inappropriately";
+    }
+
+    if not string "${unicode:ttt}" "${hex:24}{unicode:ttt}" {
+        test_fail "erroneous unicode content handled inappropriately";
+    }
+	
+	if not string "${hex:aa aa" "${hex:24}{hex:aa aa" {
+        test_fail "unterminated hex content handled inappropriately";
+    }
+
+    if not string "${unicode: aaaa aaaa" "${hex:24}{unicode: aaaa aaaa" {
+        test_fail "unterminated unicode content handled inappropriately";
+    }
+}
+
 /*
  * RFC Examples
  */