diff --git a/daemon/engine.c b/daemon/engine.c
index 21cf159040ea8221db9fca3d570412da704795d2..23d03986381b060015e8a49f6a3371dd1aa65e8a 100644
--- a/daemon/engine.c
+++ b/daemon/engine.c
@@ -232,6 +232,15 @@ static int l_option(lua_State *L)
 	return 1;
 }
 
+/** @internal for l_trustanchor: */
+static void ta_add(zs_scanner_t *zs)
+{
+	map_t *ta = zs->process.data;
+	if (!ta)
+		return;
+	if (kr_ta_add(ta, zs->r_owner, zs->r_type, zs->r_ttl, zs->r_data, zs->r_data_length))
+		zs->process.data = NULL; /* error signalling */
+}
 /** Enable/disable trust anchor. */
 static int l_trustanchor(lua_State *L)
 {
@@ -260,13 +269,11 @@ static int l_trustanchor(lua_State *L)
 		lua_pushstring(L, "not enough memory");
 		lua_error(L);
 	}
-	int ok = zs_set_input_string(zs, anchor, strlen(anchor)) == 0 &&
-	         zs_parse_all(zs) == 0;
-	/* Add it to TA set and cleanup */
-	if (ok) {
-		ok = kr_ta_add(&engine->resolver.trust_anchors,
-		               zs->r_owner, zs->r_type, zs->r_ttl, zs->r_data, zs->r_data_length) == 0;
-	}
+	zs_set_processing(zs, ta_add, NULL, &engine->resolver.trust_anchors);
+	bool ok = zs_set_input_string(zs, anchor, strlen(anchor)) == 0
+		&& zs_parse_all(zs) == 0;
+	ok = ok && zs->process.data; /* reset to NULL on error in ta_add */
+
 	zs_deinit(zs);
 	free(zs);
 	/* Report errors */
diff --git a/daemon/lua/trust_anchors.lua b/daemon/lua/trust_anchors.lua
index 52eb0d546f2b2b651547fb05ae7e9b48baf4a305..b219e6a007ba68c2c35055adb16d02940e8fc4af 100644
--- a/daemon/lua/trust_anchors.lua
+++ b/daemon/lua/trust_anchors.lua
@@ -17,7 +17,7 @@ local function https_fetch(url, ca)
 	return resp[1]
 end
 
--- Fetch root anchors in XML over HTTPS
+-- Fetch root anchors in XML over HTTPS, returning a zone-file-style string.
 local function bootstrap(url, ca)
 	-- @todo ICANN certificate is verified against current CA
 	--       this is not ideal, as it should rather verify .xml signature which
@@ -28,11 +28,14 @@ local function bootstrap(url, ca)
 	if not xml then
 		return false, string.format('[ ta ] fetch of "%s" failed: %s', url, err)
 	end
-	-- Parse root trust anchor
-	local fields = {}
-	string.gsub(xml, "<([%w]+).->([^<]+)</[%w]+>", function (k, v) fields[k] = v end)
-	local rrdata = string.format('%s %s %s %s', fields.KeyDigest, fields.Algorithm, fields.DigestType, fields.Digest)
-	local rr = string.format('%s 0 IN DS %s', fields.TrustAnchor, rrdata)
+	local rr = ''
+	-- Parse root trust anchor, one digest at a time, converting to a zone-file-style string.
+	string.gsub(xml, "<KeyDigest[^>]*>(.-)</KeyDigest>", function (xml1)
+		local fields = {}
+		string.gsub(xml1, "<([%w]+).->([^<]+)</[%w]+>", function (k, v) fields[k] = v end)
+		rr = rr .. '\n' .. string.format('. 0 IN DS %s %s %s %s',
+			fields.KeyTag, fields.Algorithm, fields.DigestType, fields.Digest)
+	end)
 	-- Add to key set, create an empty keyset file to be filled
 	print('[ ta ] warning: root anchor bootstrapped, you SHOULD check the key manually, see: '..
 	      'https://data.iana.org/root-anchors/draft-icann-dnssec-trust-anchor.html#sigs')