diff --git a/src/plugins/imap-filter-sieve/imap-filter.c b/src/plugins/imap-filter-sieve/imap-filter.c
index 8baadafcf4b99d2322d1280222ef313fdf5181bb..65c0bf7d1cfb95a67cfb92fc4cdc5038fff33359 100644
--- a/src/plugins/imap-filter-sieve/imap-filter.c
+++ b/src/plugins/imap-filter-sieve/imap-filter.c
@@ -37,6 +37,7 @@ imap_filter_mail(struct client_command_context *cmd, struct mail *mail)
 	struct client *client = cmd->client;
 	string_t *errors = NULL;
 	bool have_warnings = FALSE;
+	string_t *reply = t_str_new(128);
 	int ret;
 
 	// FIXME: return fatal error status when no mail filter activity will
@@ -44,20 +45,18 @@ imap_filter_mail(struct client_command_context *cmd, struct mail *mail)
 	ret = imap_sieve_filter_run_mail(ctx->sieve, mail,
 					 &errors, &have_warnings);
 
-	o_stream_nsend_str(client->output,
-		t_strdup_printf("* %u FILTERED (TAG %s) UID %u ",
-				mail->seq, cmd->tag, mail->uid));
+	str_printfa(reply, "* %u FILTERED (TAG %s) UID %u ",
+		    mail->seq, cmd->tag, mail->uid);
 	if (ret < 0 || have_warnings) {
-		o_stream_nsend_str(client->output,
-			t_strdup_printf("%s {%"PRIuSIZE_T"}\r\n",
-					(ret < 0 ? "ERRORS" : "WARNINGS"),
-					str_len(errors)));
-		o_stream_nsend(client->output,
-			       str_data(errors), str_len(errors));
-		o_stream_nsend_str(client->output, "\r\n");
+		str_printfa(reply, "%s {%"PRIuSIZE_T"}\r\n",
+			    (ret < 0 ? "ERRORS" : "WARNINGS"),
+			    str_len(errors));
+		str_append_str(reply, errors);
+		str_append(reply, "\r\n");
 	} else {
-		o_stream_nsend_str(client->output, "OK\r\n");
+		str_append(reply, "OK\r\n");
 	}
+	o_stream_nsend(client->output, str_data(reply), str_len(reply));
 
 	/* Handle the result */
 	if (ret < 0) {