diff --git a/src/lib-sieve/sieve-interpreter.c b/src/lib-sieve/sieve-interpreter.c
index 42f6ea1c62be0adfe0d33f36a2f10b7777c0b952..47ece881084546ed6921d18f14bf8c84675c72b3 100644
--- a/src/lib-sieve/sieve-interpreter.c
+++ b/src/lib-sieve/sieve-interpreter.c
@@ -953,7 +953,7 @@ int sieve_interpreter_continue(struct sieve_interpreter *interp,
 			sieve_runtime_error(
 				renv, NULL,
 				"execution exceeded CPU time limit");
-			ret = SIEVE_EXEC_FAILURE;
+			ret = SIEVE_EXEC_RESOURCE_LIMIT;
 			break;
 		}
 		if (interp->loop_limit != 0 && *address > interp->loop_limit) {
@@ -998,6 +998,9 @@ int sieve_interpreter_continue(struct sieve_interpreter *interp,
 		case SIEVE_EXEC_BIN_CORRUPT:
 			e->add_str("error", "Binary corrupt");
 			break;
+		case SIEVE_EXEC_RESOURCE_LIMIT:
+			e->add_str("error", "Resource limit exceeded");
+			break;
 		case SIEVE_EXEC_KEEP_FAILED:
 			/* Not supposed to occur at runtime */
 			i_unreached();
diff --git a/src/lib-sieve/sieve-types.h b/src/lib-sieve/sieve-types.h
index 5ab027e29987c880e9cfca575f5d13a5bcede573..f2e64a4cda3c0786f87c8420c354e99079666e10 100644
--- a/src/lib-sieve/sieve-types.h
+++ b/src/lib-sieve/sieve-types.h
@@ -290,11 +290,12 @@ struct sieve_exec_status {
  */
 
 enum sieve_execution_exitcode {
-	SIEVE_EXEC_OK           = 1,
-	SIEVE_EXEC_FAILURE      = 0,
-	SIEVE_EXEC_TEMP_FAILURE = -1,
-	SIEVE_EXEC_BIN_CORRUPT  = -2,
-	SIEVE_EXEC_KEEP_FAILED  = -3
+	SIEVE_EXEC_OK         	        = 1,
+	SIEVE_EXEC_FAILURE              = 0,
+	SIEVE_EXEC_TEMP_FAILURE         = -1,
+	SIEVE_EXEC_BIN_CORRUPT          = -2,
+	SIEVE_EXEC_KEEP_FAILED          = -3,
+	SIEVE_EXEC_RESOURCE_LIMIT       = -4,
 };
 
 #endif
diff --git a/src/plugins/imap-filter-sieve/imap-filter-sieve.c b/src/plugins/imap-filter-sieve/imap-filter-sieve.c
index f3e33017fa0f53cbabdd8853aea09182b21e578c..26369541ff01dd445e648a01048cb8a4752fe233 100644
--- a/src/plugins/imap-filter-sieve/imap-filter-sieve.c
+++ b/src/plugins/imap-filter-sieve/imap-filter-sieve.c
@@ -728,6 +728,14 @@ imap_sieve_filter_handle_exec_status(struct imap_filter_sieve_context *sctx,
 		*fatal_r = TRUE;
 		ret = -1;
 		break;
+	case SIEVE_EXEC_RESOURCE_LIMIT:
+		e_error(sieve_get_event(svinst),
+			"Execution of script %s was aborted "
+			"due to excessive resource usage",
+			sieve_script_location(script));
+		*fatal_r = TRUE;
+		ret = -1;
+		break;
 	case SIEVE_EXEC_KEEP_FAILED:
 		e_log(sieve_get_event(svinst), log_level,
 		      "Execution of script %s failed "
diff --git a/src/plugins/imapsieve/imap-sieve.c b/src/plugins/imapsieve/imap-sieve.c
index 74d66eb7b07cd41dabd1b7cfd6474308e457bfbf..3ed19ea8f1340e87213927e600665edf84dd79c5 100644
--- a/src/plugins/imapsieve/imap-sieve.c
+++ b/src/plugins/imapsieve/imap-sieve.c
@@ -636,6 +636,14 @@ imap_sieve_handle_exec_status(struct imap_sieve_run *isrun,
 		*fatal_r = TRUE;
 		ret = -1;
 		break;
+	case SIEVE_EXEC_RESOURCE_LIMIT:
+		e_error(sieve_get_event(svinst),
+			"Execution of script %s was aborted "
+			"due to excessive resource usage",
+			sieve_script_location(script));
+		*fatal_r = TRUE;
+		ret = -1;
+		break;
 	case SIEVE_EXEC_KEEP_FAILED:
 		e_log(sieve_get_event(svinst), log_level,
 		      "Execution of script %s failed "
diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c
index 0fa323de1f4a168d0502f40ce2a318e5f6412645..175b76ac2311e6a9cb395def508deef863167faf 100644
--- a/src/plugins/lda-sieve/lda-sieve-plugin.c
+++ b/src/plugins/lda-sieve/lda-sieve-plugin.c
@@ -564,6 +564,8 @@ lda_sieve_execute_script(struct lda_sieve_run_context *srctx,
 		if (mstatus != SIEVE_EXEC_BIN_CORRUPT)
 			lda_sieve_binary_save(srctx, sbin, script);
 	}
+	if (ret == 0 && mstatus == SIEVE_EXEC_RESOURCE_LIMIT)
+		ret = -1;
 
 	sieve_close(&sbin);
 
diff --git a/src/sieve-tools/sieve-filter.c b/src/sieve-tools/sieve-filter.c
index d71f4bcad9ef80feac3dce05668bbd1b534eed8f..deaae6abdf7db5d461bc345c973de26da431dfa6 100644
--- a/src/sieve-tools/sieve-filter.c
+++ b/src/sieve-tools/sieve-filter.c
@@ -237,6 +237,9 @@ static int filter_message(struct sieve_filter_context *sfctx, struct mail *mail)
 	switch (ret) {
 	case SIEVE_EXEC_OK:
 		break;
+	case SIEVE_EXEC_RESOURCE_LIMIT:
+		sieve_error(ehandler, NULL, "sieve resource limit exceeded");
+		return -1;
 	case SIEVE_EXEC_BIN_CORRUPT:
 		sieve_error(ehandler, NULL, "sieve script binary is corrupt");
 		return -1;
diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c
index b623b67145a5547d04e114c4279a5bc2e00f32fa..c553ace891bbbab6013e942c55d3250c26e25bff 100644
--- a/src/sieve-tools/sieve-test.c
+++ b/src/sieve-tools/sieve-test.c
@@ -452,6 +452,10 @@ int main(int argc, char **argv)
 		case SIEVE_EXEC_OK:
 			i_info("final result: success");
 			break;
+		case SIEVE_EXEC_RESOURCE_LIMIT:
+			i_info("resource limit exceeded");
+			exit_status = EXIT_FAILURE;
+			break;
 		case SIEVE_EXEC_BIN_CORRUPT:
 			i_info("corrupt binary deleted.");
 			i_unlink_if_exists(sieve_binary_path(sbin));
diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c
index 4cd4ba7228c92d1e15ef673f383f606946b2f11b..2a6b82eb45621dca7bae11b5a45163757f7c988c 100644
--- a/src/testsuite/testsuite.c
+++ b/src/testsuite/testsuite.c
@@ -222,6 +222,10 @@ int main(int argc, char **argv)
 			testsuite_testcase_fail(
 				"compiled test script binary is corrupt");
 			break;
+		case SIEVE_EXEC_RESOURCE_LIMIT:
+			testsuite_testcase_fail(
+				"resource limit exceeded");
+			break;
 		}
 
 		sieve_close(&sbin);