validation FEATURE multi-error parsing/validation

Refs #1978
diff --git a/src/parser_json.c b/src/parser_json.c
index 6219c6e..7a87af1 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -183,7 +183,7 @@
 }
 
 /**
- * @brief Skip the current JSON object/array.
+ * @brief Skip the current JSON item (baed on status).
  *
  * @param[in] jsonctx JSON context with the input data to skip.
  * @return LY_ERR value.
@@ -195,12 +195,15 @@
     uint32_t orig_depth;
 
     status = lyjson_ctx_status(jsonctx, 0);
-    assert((status == LYJSON_OBJECT) || (status == LYJSON_ARRAY));
     orig_depth = jsonctx->depth;
 
     /* next */
     LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
 
+    if ((status != LYJSON_OBJECT) && (status != LYJSON_ARRAY)) {
+        return LY_SUCCESS;
+    }
+
     if ((status == LYJSON_OBJECT) && (current != LYJSON_OBJECT) && (current != LYJSON_ARRAY)) {
         /* no nested objects */
         LY_CHECK_RET(lyjson_ctx_next(jsonctx, NULL));
@@ -922,24 +925,26 @@
 lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last,
         struct lysc_ext_instance *ext)
 {
-    if (*node_p) {
-        /* insert, keep first pointer correct */
-        if (ext) {
-            lyplg_ext_insert(parent, *node_p);
+    if (!*node_p) {
+        return;
+    }
+
+    /* insert, keep first pointer correct */
+    if (ext) {
+        lyplg_ext_insert(parent, *node_p);
+    } else {
+        lyd_insert_node(parent, first_p, *node_p, last);
+    }
+    if (first_p) {
+        if (parent) {
+            *first_p = lyd_child(parent);
         } else {
-            lyd_insert_node(parent, first_p, *node_p, last);
-        }
-        if (first_p) {
-            if (parent) {
-                *first_p = lyd_child(parent);
-            } else {
-                while ((*first_p)->prev->next) {
-                    *first_p = (*first_p)->prev;
-                }
+            while ((*first_p)->prev->next) {
+                *first_p = (*first_p)->prev;
             }
         }
-        *node_p = NULL;
     }
+    *node_p = NULL;
 }
 
 /**
@@ -1232,7 +1237,7 @@
 lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext,
         enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
 {
-    LY_ERR rc = LY_SUCCESS;
+    LY_ERR r, rc = LY_SUCCESS;
     uint32_t prev_parse_opts, prev_int_opts;
     struct ly_in in_start;
     char *val = NULL;
@@ -1262,7 +1267,9 @@
 
         /* process the anydata content */
         while (*status != LYJSON_OBJECT_CLOSED) {
-            LY_CHECK_RET(lydjson_subtree_r(lydctx, NULL, &tree, NULL));
+            r = lydjson_subtree_r(lydctx, NULL, &tree, NULL);
+            LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
             *status = lyjson_ctx_status(lydctx->jsonctx, 0);
         }
 
@@ -1271,9 +1278,11 @@
         lydctx->int_opts = prev_int_opts;
 
         /* finish linking metadata */
-        LY_CHECK_RET(lydjson_metadata_finish(lydctx, &tree));
+        r = lydjson_metadata_finish(lydctx, &tree);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
 
-        LY_CHECK_RET(lyd_create_any(snode, tree, LYD_ANYDATA_DATATREE, 1, node));
+        r = lyd_create_any(snode, tree, LYD_ANYDATA_DATATREE, 1, node);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
         break;
     case LYJSON_ARRAY_EMPTY:
         /* store the empty array */
@@ -1281,7 +1290,8 @@
             LOGMEM(lydctx->jsonctx->ctx);
             return LY_EMEM;
         }
-        LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+        r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node);
+        LY_CHECK_ERR_GOTO(r, rc = r, val_err);
         break;
     case LYJSON_ARRAY:
         /* skip until the array end */
@@ -1293,7 +1303,8 @@
             LOGMEM(lydctx->jsonctx->ctx);
             return LY_EMEM;
         }
-        LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+        r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node);
+        LY_CHECK_ERR_GOTO(r, rc = r, val_err);
         break;
     case LYJSON_STRING:
         /* string value */
@@ -1304,7 +1315,8 @@
             val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len);
             LY_CHECK_ERR_RET(!val, LOGMEM(lydctx->jsonctx->ctx), LY_EMEM);
 
-            LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node), val_err);
+            r = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node);
+            LY_CHECK_ERR_GOTO(r, rc = r, val_err);
         }
         break;
     case LYJSON_NUMBER:
@@ -1315,21 +1327,25 @@
         val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len);
         LY_CHECK_ERR_RET(!val, LOGMEM(lydctx->jsonctx->ctx), LY_EMEM);
 
-        LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+        r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node);
+        LY_CHECK_ERR_GOTO(r, rc = r, val_err);
         break;
     case LYJSON_NULL:
         /* no value */
-        LY_CHECK_RET(lyd_create_any(snode, NULL, LYD_ANYDATA_JSON, 1, node));
+        r = lyd_create_any(snode, NULL, LYD_ANYDATA_JSON, 1, node);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
         break;
     case LYJSON_OBJECT_EMPTY:
         /* empty object */
-        LY_CHECK_RET(lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node));
+        r = lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
         break;
     default:
         LOGINT_RET(lydctx->jsonctx->ctx);
     }
 
-    return LY_SUCCESS;
+cleanup:
+    return rc;
 
 val_err:
     free(val);
@@ -1352,7 +1368,7 @@
 lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext,
         enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
 {
-    LY_ERR ret = LY_SUCCESS;
+    LY_ERR r, rc = LY_SUCCESS;
     uint32_t prev_parse_opts = lydctx->parse_opts;
 
     LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_ENOT);
@@ -1370,37 +1386,37 @@
 
     /* process children */
     while ((*status != LYJSON_OBJECT_CLOSED) && (*status != LYJSON_OBJECT_EMPTY)) {
-        ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
-        LY_CHECK_GOTO(ret, cleanup);
+        r = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
+        LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
         *status = lyjson_ctx_status(lydctx->jsonctx, 0);
     }
 
     /* finish linking metadata */
-    ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node));
-    LY_CHECK_GOTO(ret, cleanup);
+    r = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node));
+    LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
 
     if (snode->nodetype == LYS_LIST) {
         /* check all keys exist */
-        ret = lyd_parse_check_keys(*node);
-        LY_CHECK_GOTO(ret, cleanup);
+        r = lyd_parse_check_keys(*node);
+        LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
     }
 
     if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
         /* new node validation, autodelete CANNOT occur, all nodes are new */
-        ret = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, NULL);
-        LY_CHECK_GOTO(ret, cleanup);
+        r = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, lydctx->val_opts, NULL);
+        LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
 
         /* add any missing default children */
-        ret = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when,
-                &lydctx->node_types, &lydctx->ext_node,
-                (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
-        LY_CHECK_GOTO(ret, cleanup);
+        r = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
+                &lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
     }
 
 cleanup:
     lydctx->parse_opts = prev_parse_opts;
     LOG_LOCBACK(0, 1, 0, 0);
-    return ret;
+    return rc;
 }
 
 /**
@@ -1427,52 +1443,61 @@
         const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len,
         const char *prefix, size_t prefix_len, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
 {
-    LY_ERR ret = LY_SUCCESS;
+    LY_ERR r, rc = LY_SUCCESS;
     uint32_t type_hints = 0;
 
     LOG_LOCSET(snode, NULL, NULL, NULL);
 
-    ret = lydjson_data_check_opaq(lydctx, snode, &type_hints);
-    if (ret == LY_SUCCESS) {
+    r = lydjson_data_check_opaq(lydctx, snode, &type_hints);
+    if (r == LY_SUCCESS) {
         assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY));
         if (snode->nodetype & LYD_NODE_TERM) {
             if ((*status != LYJSON_ARRAY) && (*status != LYJSON_NUMBER) && (*status != LYJSON_STRING) &&
                     (*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) && (*status != LYJSON_NULL)) {
-                ret = LY_ENOT;
+                rc = LY_ENOT;
                 goto cleanup;
             }
 
             /* create terminal node */
-            LY_CHECK_GOTO(ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value,
-                    lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node), cleanup);
+            r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value,
+                    lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node);
+            LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
 
             /* move JSON parser */
             if (*status == LYJSON_ARRAY) {
                 /* only [null], 2 more moves are needed */
-                LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status), cleanup);
+                r = lyjson_ctx_next(lydctx->jsonctx, status);
+                LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
                 assert(*status == LYJSON_NULL);
-                LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status), cleanup);
+
+                r = lyjson_ctx_next(lydctx->jsonctx, status);
+                LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
                 assert(*status == LYJSON_ARRAY_CLOSED);
             }
         } else if (snode->nodetype & LYD_NODE_INNER) {
             /* create inner node */
-            LY_CHECK_GOTO(ret = lydjson_parse_instance_inner(lydctx, snode, ext, status, node), cleanup);
+            r = lydjson_parse_instance_inner(lydctx, snode, ext, status, node);
+            LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
         } else {
             /* create any node */
-            LY_CHECK_GOTO(ret = lydjson_parse_any(lydctx, snode, ext, status, node), cleanup);
+            r = lydjson_parse_any(lydctx, snode, ext, status, node);
+            LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
         }
+        LY_CHECK_GOTO(!*node, cleanup);
 
         /* add/correct flags */
-        LY_CHECK_GOTO(ret = lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext), cleanup);
+        r = lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
 
         if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
             /* store for ext instance node validation, if needed */
-            LY_CHECK_GOTO(ret = lyd_validate_node_ext(*node, &lydctx->ext_node), cleanup);
+            r = lyd_validate_node_ext(*node, &lydctx->ext_node);
+            LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
         }
-    } else if (ret == LY_ENOT) {
+    } else if (r == LY_ENOT) {
         /* parse it again as an opaq node */
-        LY_CHECK_GOTO(ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status,
-                first_p, node), cleanup);
+        r = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status, first_p, node);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
 
         if (snode->nodetype == LYS_LIST) {
             ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LIST;
@@ -1486,7 +1511,7 @@
 
 cleanup:
     LOG_LOCBACK(1, 0, 0, 0);
-    return ret;
+    return rc;
 }
 
 /**
@@ -1501,7 +1526,7 @@
 static LY_ERR
 lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
 {
-    LY_ERR ret = LY_SUCCESS, r;
+    LY_ERR r, rc = LY_SUCCESS;
     enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx, 0);
     const char *name, *prefix = NULL, *expected = NULL;
     size_t name_len, prefix_len = 0;
@@ -1530,7 +1555,7 @@
             /* data parsed */
             goto cleanup;
         }
-        LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
 
         if (!snode) {
             /* we will not be parsing it as metadata */
@@ -1540,20 +1565,19 @@
 
     if (is_meta) {
         /* parse as metadata */
-        if (!name_len && !prefix_len) {
+        if (!name_len && !prefix_len && !parent) {
+            LOGVAL(ctx, LYVE_SYNTAX_JSON,
+                    "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries.");
+            r = LY_EVALID;
+            LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+        } else if (!name_len && !prefix_len) {
             /* parent's metadata without a name - use the schema from the parent */
-            if (!parent) {
-                LOGVAL(ctx, LYVE_SYNTAX_JSON,
-                        "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries.");
-                ret = LY_EVALID;
-                goto cleanup;
-            }
             attr_node = parent;
             snode = attr_node->schema;
         }
-        ret = lydjson_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, &status,
+        r = lydjson_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, &status,
                 first_p, &node);
-        LY_CHECK_GOTO(ret, cleanup);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
     } else if (!snode) {
         /* parse as an opaq node */
         assert((lydctx->parse_opts & LYD_PARSE_OPAQ) || (lydctx->int_opts));
@@ -1561,18 +1585,19 @@
         /* opaq node cannot have an empty string as the name. */
         if (name_len == 0) {
             LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "A JSON object member name cannot be a zero-length string.");
-            ret = LY_EVALID;
-            goto cleanup;
+            r = LY_EVALID;
+            LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
         }
 
         /* move to the second item in the name/X pair and parse opaq */
-        ret = lydjson_ctx_next_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, &status, first_p, &node);
-        LY_CHECK_GOTO(ret, cleanup);
+        r = lydjson_ctx_next_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, &status, first_p, &node);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
     } else {
         /* parse as a standard lyd_node but it can still turn out to be an opaque node */
 
         /* move to the second item in the name/X pair */
-        LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+        r = lyjson_ctx_next(lydctx->jsonctx, &status);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
 
         /* set expected representation */
         switch (snode->nodetype) {
@@ -1616,22 +1641,23 @@
             LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
 
             /* move into array */
-            ret = lyjson_ctx_next(lydctx->jsonctx, &status);
-            LY_CHECK_GOTO(ret, cleanup);
+            r = lyjson_ctx_next(lydctx->jsonctx, &status);
+            LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
 
             /* process all the values/objects */
             do {
-                ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
+                r = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
                         &status, &node);
-                if (ret == LY_ENOT) {
+                if (r == LY_ENOT) {
                     goto representation_error;
-                } else if (ret) {
-                    goto cleanup;
                 }
+                LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
                 lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
 
                 /* move after the item(s) */
-                LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+                r = lyjson_ctx_next(lydctx->jsonctx, &status);
+                LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
             } while (status != LYJSON_ARRAY_CLOSED);
 
             break;
@@ -1643,13 +1669,12 @@
         case LYS_ANYDATA:
         case LYS_ANYXML:
             /* process the value/object */
-            ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
+            r = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
                     &status, &node);
-            if (ret == LY_ENOT) {
+            if (r == LY_ENOT) {
                 goto representation_error;
-            } else if (ret) {
-                goto cleanup;
             }
+            LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
 
             if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
                 /* rememeber the RPC/action/notification */
@@ -1669,7 +1694,8 @@
 
     if (!parse_subtree) {
         /* move after the item(s) */
-        LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+        r = lyjson_ctx_next(lydctx->jsonctx, &status);
+        LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
     }
 
     /* success */
@@ -1678,12 +1704,16 @@
 representation_error:
     LOGVAL(ctx, LYVE_SYNTAX_JSON, "The %s \"%s\" is expected to be represented as JSON %s, but input data contains name/%s.",
             lys_nodetype2str(snode->nodetype), snode->name, expected, lyjson_token2str(status));
-    ret = LY_EVALID;
+    rc = LY_EVALID;
+    if (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) {
+        /* try to skip the invalid data */
+        lydjson_data_skip(lydctx->jsonctx);
+    }
 
 cleanup:
     free(value);
     lyd_free_tree(node);
-    return ret;
+    return rc;
 }
 
 /**
@@ -1745,7 +1775,7 @@
         struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
         struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
 {
-    LY_ERR rc = LY_SUCCESS;
+    LY_ERR r, rc = LY_SUCCESS;
     struct lyd_json_ctx *lydctx = NULL;
     enum LYJSON_PARSER_STATUS status;
 
@@ -1762,8 +1792,8 @@
 
     /* read subtree(s) */
     while (lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) {
-        rc = lydjson_subtree_r(lydctx, parent, first_p, parsed);
-        LY_CHECK_GOTO(rc, cleanup);
+        r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
+        LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
 
         status = lyjson_ctx_status(lydctx->jsonctx, 0);
 
@@ -1775,18 +1805,18 @@
     if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] &&
             (lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_OBJECT_CLOSED)) {
         LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
-        rc = LY_EVALID;
-        goto cleanup;
+        r = LY_EVALID;
+        LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
     }
     if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
         LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
-        rc = LY_EVALID;
-        goto cleanup;
+        r = LY_EVALID;
+        LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
     }
 
     /* finish linking metadata */
-    rc = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
-    LY_CHECK_GOTO(rc, cleanup);
+    r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
+    LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
 
     if (parse_opts & LYD_PARSE_SUBTREE) {
         /* check for a sibling object */
@@ -1806,7 +1836,7 @@
     assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count &&
             !lydctx->node_when.count));
 
-    if (rc) {
+    if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
         lyd_json_ctx_free((struct lyd_ctx *)lydctx);
     } else {
         *lydctx_p = (struct lyd_ctx *)lydctx;