diff --git a/doc/man/kzonecheck.1in b/doc/man/kzonecheck.1in
index 9dea313953bb97925368ddf827c7a71839a7632f..559fafee3ff3a8c9d478cc989528aa74aeaeda76 100644
--- a/doc/man/kzonecheck.1in
+++ b/doc/man/kzonecheck.1in
@@ -40,7 +40,7 @@ This utility checks zone similar to knotc zonecheck, but without running server.
 .INDENT 0.0
 .TP
 \fB\-o\fP, \fB\-\-origin\fP \fIorigin\fP
-The zone origin. If not specified the name of file or name without .zone ending is assumed to be the origin.
+The zone origin. If not specified the name of file or name without \(aq.zone\(aq ending is assumed to be the origin.
 .TP
 \fB\-v\fP, \fB\-\-verbose\fP
 Enable debug output.
diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c
index ec86a347b712e442e55c898cf8c638184c37a9ac..1d20993524f52d065294bc5c1118a796461a4f1b 100644
--- a/src/knot/zone/semantic-check.c
+++ b/src/knot/zone/semantic-check.c
@@ -37,8 +37,6 @@ const char *zonechecks_error_messages[(-ZC_ERR_UNKNOWN) + 1] = {
 	"SOA record missing in zone",
 	[-ZC_ERR_MISSING_NS_DEL_POINT] =
 	"NS record missing in zone apex",
-	[-ZC_ERR_TTL_MISMATCH] =
-	"RRSet TTLs mismatched",
 
 	[-ZC_ERR_RRSIG_RDATA_TYPE_COVERED] =
 	"RRSIG, type covered RDATA field is wrong",
@@ -165,7 +163,7 @@ void err_handler_deinit(err_handler_t *h)
 	}
 }
 
-int err_handler_handle_error(err_handler_t *handler, const zone_contents_t *zone,
+int err_handler_node_error(err_handler_t *handler, const zone_contents_t *zone,
 			     const zone_node_t *node, int error, const char *data)
 {
 	err_node_t * log = malloc(sizeof(err_node_t));
@@ -188,6 +186,26 @@ int err_handler_handle_error(err_handler_t *handler, const zone_contents_t *zone
 	return KNOT_EOK;
 }
 
+int err_handler_zone_error(err_handler_t *handler, const knot_dname_t *zname,
+                           int error)
+{
+	err_node_t * log = malloc(sizeof(err_node_t));
+	if (log == NULL) {
+		return KNOT_ENOMEM;
+	}
+	log->error = error;
+	log->data = NULL;
+	log->name = NULL;
+
+	log->zone_name = knot_dname_to_str_alloc(zname);
+
+	add_tail(&handler->error_list, (node_t*)log);
+	handler->errors[-error]++;
+	handler->error_count++;
+
+	return KNOT_EOK;
+}
+
 void err_handler_log_errors(err_handler_t *handler)
 {
 	err_node_t *n;
@@ -261,7 +279,7 @@ static int check_rrsig_rdata(err_handler_t *handler,
 	}
 
 	if (knot_rrsig_type_covered(rrsig, 0) != rrset->type) {
-		err_handler_handle_error(handler, zone, node,
+		err_handler_node_error(handler, zone, node,
 		                         ZC_ERR_RRSIG_RDATA_TYPE_COVERED,
 		                         info_str);
 	}
@@ -274,12 +292,12 @@ static int check_rrsig_rdata(err_handler_t *handler,
 	if (tmp != 0) {
 		/* if name has wildcard, label must not be included */
 		if (!knot_dname_is_wildcard(rrset->owner)) {
-			err_handler_handle_error(handler, zone, node,
+			err_handler_node_error(handler, zone, node,
 			                         ZC_ERR_RRSIG_RDATA_LABELS,
 			                         info_str);
 		} else {
 			if (abs(tmp) != 1) {
-				err_handler_handle_error(handler, zone, node,
+				err_handler_node_error(handler, zone, node,
 				             ZC_ERR_RRSIG_RDATA_LABELS,
 				                         info_str);
 			}
@@ -292,7 +310,7 @@ static int check_rrsig_rdata(err_handler_t *handler,
 	uint16_t rr_count = rrset->rrs.rr_count;
 	for (uint16_t i = 0; i < rr_count; ++i) {
 		if (original_ttl != knot_rdata_ttl(knot_rdataset_at(&rrset->rrs, i))) {
-			err_handler_handle_error(handler, zone, node,
+			err_handler_node_error(handler, zone, node,
 			                         ZC_ERR_RRSIG_RDATA_TTL,
 			                         info_str);
 		}
@@ -300,14 +318,14 @@ static int check_rrsig_rdata(err_handler_t *handler,
 
 	/* Check for expired signature. */
 	if (knot_rrsig_sig_expiration(rrsig, rr_pos) < time(NULL)) {
-		err_handler_handle_error(handler, zone, node,
+		err_handler_node_error(handler, zone, node,
 		                         ZC_ERR_RRSIG_RDATA_EXPIRATION,
 		                         info_str);
 	}
 
 	/* Check if DNSKEY exists. */
 	if (knot_rrset_empty(dnskey_rrset)) {
-		err_handler_handle_error(handler, zone, node,
+		err_handler_node_error(handler, zone, node,
 		                         ZC_ERR_RRSIG_NO_DNSKEY, info_str);
 	}
 
@@ -317,7 +335,7 @@ static int check_rrsig_rdata(err_handler_t *handler,
 	/* dnskey is in the apex node */
 	if (!knot_rrset_empty(dnskey_rrset) &&
 	    !knot_dname_is_equal(signer, dnskey_rrset->owner)) {
-		err_handler_handle_error(handler, zone, node,
+		err_handler_node_error(handler, zone, node,
 		                         ZC_ERR_RRSIG_RDATA_DNSKEY_OWNER,
 		                         info_str);
 	}
@@ -355,14 +373,14 @@ static int check_rrsig_rdata(err_handler_t *handler,
 		if (check_dnskey_rdata(dnskey_rrset, i) == KNOT_EOK) {
 			match = 1;
 		} else {
-			err_handler_handle_error(handler, zone, node,
+			err_handler_node_error(handler, zone, node,
 			                         ZC_ERR_RRSIG_RDATA_SIGNED_WRONG,
 			                         "DNSKEY RDATA not matching");
 		}
 	}
 
 	if (!match) {
-		err_handler_handle_error(handler, zone, node,
+		err_handler_node_error(handler, zone, node,
 		                         ZC_ERR_RRSIG_NO_DNSKEY, info_str);
 	}
 
@@ -373,7 +391,7 @@ static void check_signed_rrsig(const zone_node_t *node, semchecks_data_t *data)
 {
 	/* signed rrsig - nonsense */
 	if (node_rrtype_is_signed(node, KNOT_RRTYPE_RRSIG)) {
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_RRSIG_SIGNED, NULL);
 	}
 }
@@ -416,7 +434,7 @@ static int check_rrsig_in_rrset(err_handler_t *handler,
 	}
 
 	if (ret == KNOT_ENOENT) {
-		err_handler_handle_error(handler, zone, node,
+		err_handler_node_error(handler, zone, node,
 		                         ZC_ERR_RRSIG_NO_RRSIG,
 		                         info_str);
 		return KNOT_EOK;
@@ -424,7 +442,7 @@ static int check_rrsig_in_rrset(err_handler_t *handler,
 
 	const knot_rdata_t *sig_rr = knot_rdataset_at(&rrsigs, 0);
 	if (knot_rdata_ttl(knot_rdataset_at(&rrset->rrs, 0)) != knot_rdata_ttl(sig_rr)) {
-		err_handler_handle_error(handler, zone, node,
+		err_handler_node_error(handler, zone, node,
 		                         ZC_ERR_RRSIG_TTL,
 		                         info_str);
 	}
@@ -455,7 +473,7 @@ static void check_delegation(const zone_node_t *node, semchecks_data_t *data)
 	}
 	const knot_rdataset_t *ns_rrs = node_rdataset(node, KNOT_RRTYPE_NS);
 	if (ns_rrs == NULL) {
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_MISSING_NS_DEL_POINT,
 		                         NULL);
 		return;
@@ -482,7 +500,7 @@ static void check_delegation(const zone_node_t *node, semchecks_data_t *data)
 			const zone_node_t *wildcard_node =
 				zone_contents_find_node(data->zone, wildcard);
 			if (wildcard_node == NULL) {
-				err_handler_handle_error(
+				err_handler_node_error(
 					data->handler, data->zone, node,
 					ZC_ERR_GLUE_NODE, NULL);
 				// Cannot continue
@@ -492,7 +510,7 @@ static void check_delegation(const zone_node_t *node, semchecks_data_t *data)
 		}
 		if (!node_rrtype_exists(glue_node, KNOT_RRTYPE_A) &&
 		    !node_rrtype_exists(glue_node, KNOT_RRTYPE_AAAA)) {
-			err_handler_handle_error(data->handler, data->zone,
+			err_handler_node_error(data->handler, data->zone,
 			                         node, ZC_ERR_GLUE_RECORD, NULL);
 		}
 	}
@@ -601,7 +619,7 @@ static void check_nsec_bitmap(const zone_node_t *node, semchecks_data_t *data)
 
 	if (node_wire_size != nsec_wire_size ||
 	    memcmp(node_wire, nsec_wire, node_wire_size) != 0) {
-		err_handler_handle_error(data->handler,
+		err_handler_node_error(data->handler,
 		                         data->zone, node,
 		                         ZC_ERR_NSEC_RDATA_BITMAP,
 		                         NULL);
@@ -628,21 +646,21 @@ static void check_nsec(const zone_node_t *node, semchecks_data_t *data)
 	const knot_rdataset_t *nsec_rrs =
 		node_rdataset(node, KNOT_RRTYPE_NSEC);
 	if (nsec_rrs == NULL) {
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_NO_NSEC, NULL);
 		return;
 	}
 
 	/* Test that only one record is in the NSEC RRSet */
 	if (nsec_rrs->rr_count != 1) {
-		err_handler_handle_error(data->handler,
+		err_handler_node_error(data->handler,
 		                         data->zone, node,
 		                         ZC_ERR_NSEC_RDATA_MULTIPLE,
 		                         NULL);
 	}
 
 	if (data->next_nsec != node ) {
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_NSEC_RDATA_CHAIN,
 		                         NULL);
 	}
@@ -659,7 +677,7 @@ static void check_nsec(const zone_node_t *node, semchecks_data_t *data)
 	data->next_nsec = zone_contents_find_node(data->zone, next_domain);
 
 	if (data->next_nsec == NULL) {
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_NSEC_RDATA_CHAIN,
 		                         NULL);
 	}
@@ -681,7 +699,7 @@ static void check_nsec3_opt_out(const zone_node_t *node, semchecks_data_t *data)
 	/* I know it's probably not what RFCs say, but it will have to
 	 * do for now. */
 	if (node_rrtype_exists(node, KNOT_RRTYPE_DS)) {
-		err_handler_handle_error(
+		err_handler_node_error(
 				data->handler, data->zone, node,
 				ZC_ERR_NSEC3_UNSECURED_DELEGATION, NULL);
 		return;
@@ -694,7 +712,7 @@ static void check_nsec3_opt_out(const zone_node_t *node, semchecks_data_t *data)
 		                                      node->owner,
 		                                      &nsec3_node,
 		                                      &nsec3_previous) != ZONE_NAME_NOT_FOUND) {
-			err_handler_handle_error(data->handler,
+			err_handler_node_error(data->handler,
 			                         data->zone, node,
 			                         ZC_ERR_NSEC3_NOT_FOUND,
 			                         NULL);
@@ -715,7 +733,7 @@ static void check_nsec3_opt_out(const zone_node_t *node, semchecks_data_t *data)
 			// opt-out no need to check more
 			return;
 		} else {
-			err_handler_handle_error(data->handler,
+			err_handler_node_error(data->handler,
 				data->zone, node,
 				ZC_ERR_NSEC3_UNSECURED_DELEGATION_OPT,
 				NULL);
@@ -751,7 +769,7 @@ static void check_nsec3(const zone_node_t *node, semchecks_data_t *data)
 	const knot_rdataset_t *nsec3_rrs = node_rdataset(nsec3_node,
 	                                            KNOT_RRTYPE_NSEC3);
 	if (nsec3_rrs == NULL) {
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_NSEC3_RDATA_CHAIN, NULL);
 		return;
 	}
@@ -761,7 +779,7 @@ static void check_nsec3(const zone_node_t *node, semchecks_data_t *data)
 	assert(soa_rrs);
 	uint32_t minimum_ttl = knot_soa_minimum(soa_rrs);
 	if (knot_rdata_ttl(nsec3_rr) != minimum_ttl) {
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_NSEC3_RDATA_TTL, NULL);
 	}
 
@@ -784,10 +802,10 @@ static void check_nsec3(const zone_node_t *node, semchecks_data_t *data)
 	knot_dname_free(&next_dname, NULL);
 
 	if (next_nsec3 == NULL) {
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_NSEC3_RDATA_CHAIN, NULL);
 	} else if (next_nsec3->prev != nsec3_node) {
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_NSEC3_RDATA_CHAIN, NULL);
 	}
 
@@ -797,7 +815,7 @@ static void check_nsec3(const zone_node_t *node, semchecks_data_t *data)
 		uint16_t type = rrset.type;
 		if (!(type == KNOT_RRTYPE_NSEC3 ||
 		    type == KNOT_RRTYPE_RRSIG)) {
-			err_handler_handle_error(data->handler, data->zone, nsec3_node,
+			err_handler_node_error(data->handler, data->zone, nsec3_node,
 			                         ZC_ERR_NSEC3_EXTRA_RECORD,
 			                         NULL);
 		}
@@ -827,7 +845,7 @@ static void check_cname_multiple(const zone_node_t *node, semchecks_data_t *data
 
 	if (node->rrset_count > rrset_limit) {
 		data->fatal_error = true;
-		err_handler_handle_error(data->handler,
+		err_handler_node_error(data->handler,
 		data->zone, node,
 		rrset_limit > 1 ?
 		ZC_ERR_CNAME_EXTRA_RECORDS_DNSSEC :
@@ -835,7 +853,7 @@ static void check_cname_multiple(const zone_node_t *node, semchecks_data_t *data
 	}
 	if (cname_rrs->rr_count != 1) {
 		data->fatal_error = true;
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_CNAME_MULTIPLE, NULL);
 	}
 }
@@ -852,14 +870,14 @@ static void check_dname(const zone_node_t *node, semchecks_data_t *data)
 
 	if (dname_rrs != NULL && node->children != 0) {
 		data->fatal_error = true;
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_DNAME_CHILDREN,
 		                         "error triggered by parent node");
 	}
 
 	if (node->parent != NULL && node_rrtype_exists(node->parent, KNOT_RRTYPE_DNAME)) {
 		data->fatal_error = true;
-		err_handler_handle_error(data->handler, data->zone, node,
+		err_handler_node_error(data->handler, data->zone, node,
 		                         ZC_ERR_DNAME_CHILDREN,
 		                         "error triggered by child node");
 	}
@@ -874,14 +892,14 @@ static void check_dname(const zone_node_t *node, semchecks_data_t *data)
 static void check_nsec_cyclic(semchecks_data_t *data)
 {
 	if (data->next_nsec == NULL) {
-		err_handler_handle_error(data->handler, data->zone,
+		err_handler_node_error(data->handler, data->zone,
 		                         data->zone->apex,
 		                         ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC, NULL);
 		return;
 	}
 
 	if (!knot_dname_is_equal(data->next_nsec->owner, data->zone->apex->owner)) {
-		err_handler_handle_error(data->handler, data->zone, data->next_nsec,
+		err_handler_node_error(data->handler, data->zone, data->next_nsec,
 		                         ZC_ERR_NSEC_RDATA_CHAIN_NOT_CYCLIC, NULL);
 		return;
 	}
diff --git a/src/knot/zone/semantic-check.h b/src/knot/zone/semantic-check.h
index a34e1f2641c08ded7250f516b3eb0b725c134c61..3254741295f3c4e433e6ab8ed3bd6c1e07137eea 100644
--- a/src/knot/zone/semantic-check.h
+++ b/src/knot/zone/semantic-check.h
@@ -46,7 +46,6 @@ enum zonechecks_errors {
 
 	ZC_ERR_MISSING_SOA,
 	ZC_ERR_MISSING_NS_DEL_POINT,
-	ZC_ERR_TTL_MISMATCH,
 
 	ZC_ERR_GENERIC_GENERAL_ERROR, /* Generic error delimiter. */
 
@@ -148,11 +147,26 @@ void err_handler_deinit(err_handler_t *h);
  * \retval KNOT_EOK on success.
  * \retval KNOT_ENOMEM if memory error.
  */
-int err_handler_handle_error(err_handler_t *handler,
+int err_handler_node_error(err_handler_t *handler,
                              const zone_contents_t *zone,
                              const zone_node_t *node,
                              int error, const char *data);
 
+/*!
+ * \brief Called when error has been encountered for entire zone.
+ *
+ * Will save error to list for future possibility to log it.
+ *
+ * \param handler Error handler.
+ * \param zname Zone name
+ * \param error Type of error.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ENOMEM if memory error.
+ */
+int err_handler_zone_error(err_handler_t *handler, const knot_dname_t *zname,
+                           int error);
+
 /*!
  * \brief Check zone for semantic errors.
  *
diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c
index 3fc55c183dfbb3138092911d7ccc6794d46a8a9c..aa48f0264b717b8d159ebe51f2744c00a61267c2 100644
--- a/src/knot/zone/zonefile.c
+++ b/src/knot/zone/zonefile.c
@@ -56,24 +56,20 @@ static int add_rdata_to_rr(knot_rrset_t *rrset, const zs_scanner_t *scanner)
 	                            scanner->r_ttl, NULL);
 }
 
-static void log_ttl_error(const zone_contents_t *zone, const zone_node_t *node,
-                          const knot_rrset_t *rr)
+static void log_ttl_error(const zcreator_t *zc, const zone_node_t *node,
+                          const knot_rrset_t *rr, const knot_dname_t *zone_name)
 {
-	err_handler_t err_handler;
-	err_handler_init(&err_handler);
-	// Prepare additional info string.
-	char info_str[64] = { '\0' };
+	// Prepare additional type string.
 	char type_str[16] = { '\0' };
 	knot_rrtype_to_string(rr->type, type_str, sizeof(type_str));
-	int ret = snprintf(info_str, sizeof(info_str), "record type %s",
-	                   type_str);
-	if (ret <= 0 || ret >= sizeof(info_str)) {
-		*info_str = '\0';
-	}
 
-	/*!< \todo REPLACE WITH FATAL ERROR for master. */
-	err_handler_handle_error(&err_handler, zone, node,
-	                         ZC_ERR_TTL_MISMATCH, info_str);
+	char *node_name = knot_dname_to_str_alloc(node->owner);
+
+	WARNING(zone_name, "RRSet TTLs mismatched, node '%s' (record type %s)",
+	        node_name == NULL ? "?" : node_name,
+	        type_str);
+
+	free(node_name);
 }
 
 static bool handle_err(zcreator_t *zc, const zone_node_t *node,
@@ -89,7 +85,7 @@ static bool handle_err(zcreator_t *zc, const zone_node_t *node,
 	} else if (ret == KNOT_ETTL) {
 		free(rrname);
 		assert(node);
-		log_ttl_error(zc->z, node, rr);
+		log_ttl_error(zc, node, rr, zname);
 		// Fail if we're the master for this zone.
 		return !master;
 	} else {
@@ -258,7 +254,7 @@ zone_contents_t *zonefile_load(zloader_t *loader)
 	}
 
 	if (!node_rrtype_exists(loader->creator->z->apex, KNOT_RRTYPE_SOA)) {
-		ERROR(zname, "no SOA record, file '%s'", loader->source);
+		err_handler_zone_error(&loader->err_handler, zname, ZC_ERR_MISSING_SOA);
 		goto fail;
 	}