schema compile CHANGE provide path when reporting schema compilation error
diff --git a/src/common.h b/src/common.h
index 96132b4..afab791 100644
--- a/src/common.h
+++ b/src/common.h
@@ -193,8 +193,8 @@
 #define LY_VCODE_INREGEXP    LYVE_SYNTAX_YANG, "Regular expression \"%s\" is not valid (\"%s\": %s)."
 #define LY_VCODE_XP_EOE      LYVE_XPATH, "Unterminated string delimited with %c (%.15s)."
 #define LY_VCODE_XP_INEXPR   LYVE_XPATH, "Invalid character number %u of expression \'%.*s\'."
-#define LY_VCODE_DEV_NODETYPE LYVE_REFERENCE, "Invalid deviation (%s) of %s node - it is not possible to %s \"%s\" property."
-#define LY_VCODE_DEV_NOT_PRESENT LYVE_REFERENCE, "Invalid deviation (%s) %s \"%s\" property \"%s\" which is not present."
+#define LY_VCODE_DEV_NODETYPE LYVE_REFERENCE, "Invalid deviation of %s node - it is not possible to %s \"%s\" property."
+#define LY_VCODE_DEV_NOT_PRESENT LYVE_REFERENCE, "Invalid deviation %s \"%s\" property \"%s\" which is not present."
 
 /******************************************************************************
  * Context
diff --git a/src/tree.h b/src/tree.h
index d700a5b..65c2405 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -20,6 +20,13 @@
 #endif
 
 /**
+ * @brief Types of the different schema and data paths.
+ */
+typedef enum {
+    LY_PATH_LOG /**< Descriptive path format used in log messages */
+} LY_PATH_TYPE;
+
+/**
  * @brief Macro selector for other LY_ARRAY_* macros, do not use directly!
  */
 #define LY_ARRAY_SELECT(_1, _2, NAME, ...) NAME
diff --git a/src/tree_schema.c b/src/tree_schema.c
index d1f1b76..24e38b3 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -216,6 +216,58 @@
     return NULL;
 }
 
+API char *
+lysc_path(struct lysc_node *node, LY_PATH_TYPE pathtype)
+{
+    struct lysc_node *iter;
+    char *path = NULL;
+    int len = 0;
+
+    switch (pathtype) {
+    case LY_PATH_LOG:
+        for (iter = node; iter && len >= 0; iter = iter->parent) {
+            char *s = path;
+            char *id;
+
+            switch (iter->nodetype) {
+            case LYS_USES:
+                asprintf(&id, "{uses='%s'}", iter->name);
+                break;
+            case LYS_GROUPING:
+                asprintf(&id, "{grouping='%s'}", iter->name);
+                break;
+            case LYS_AUGMENT:
+                asprintf(&id, "{augment='%s'}", iter->name);
+                break;
+            default:
+                id = strdup(iter->name);
+                break;
+            }
+
+            if (!iter->parent || iter->parent->module != iter->module) {
+                /* print prefix */
+                len = asprintf(&path, "/%s:%s%s", iter->module->name, id, s ? s : "");
+            } else {
+                /* prefix is the same as in parent */
+                len = asprintf(&path, "/%s%s", id, s ? s : "");
+            }
+            free(s);
+            free(id);
+        }
+
+        if (len < 0) {
+            free(path);
+            path = NULL;
+        } else if (len == 0) {
+            path = strdup("/");
+            len = 1;
+        }
+        break;
+    }
+
+    return path;
+}
+
 API int
 lysc_feature_value(const struct lysc_feature *feature)
 {
@@ -673,7 +725,7 @@
 
     if (!mod->implemented) {
         /* pre-compile features of the module */
-        LY_CHECK_GOTO(lys_feature_precompile(ctx, mod, mod->parsed->features, &mod->off_features), error);
+        LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, mod->parsed->features, &mod->off_features), error);
     }
 
     /* decide the latest revision */
@@ -719,7 +771,7 @@
         }
         if (!mod->implemented) {
             /* pre-compile features of the module */
-            LY_CHECK_GOTO(lys_feature_precompile(ctx, mod, inc->submodule->features, &mod->off_features), error);
+            LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, inc->submodule->features, &mod->off_features), error);
         }
     }
     mod->parsed->parsing = 0;
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 04f7d18..f5ddc35 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1436,6 +1436,16 @@
 int lysc_feature_value(const struct lysc_feature *feature);
 
 /**
+ * @brief Generate path of the given node in the requested format.
+ *
+ * @param[in] node Schema path of this node will be generated.
+ * @param[in] pathtype Format of the path to generate.
+ * @return NULL in case of memory allocation error, path of the node otherwise.
+ * Returned string is dynamically allocated and caller is responsible to free it.
+ */
+char *lysc_path(struct lysc_node *node, LY_PATH_TYPE pathtype);
+
+/**
  * @brief Available YANG schema tree structures representing YANG module.
  */
 struct lys_module {
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 68c219f..e872d6e 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -109,6 +109,66 @@
 }
 
 /**
+ * @brief Update path in the compile context.
+ *
+ * @param[in] ctx Compile context with the path.
+ * @param[in] parent Parent of the current node to check difference of the node's module. The current module is taken from lysc_ctx::mod.
+ * @param[in] name Name of the node to update path with. If NULL, the last segment is removed. If the format is `{keyword}`, the following
+ * call updates the segment to the form `{keyword='name'}` (to remove this compound segment, 2 calls with NULL @p name must be used).
+ */
+static void
+lysc_update_path(struct lysc_ctx *ctx, struct lysc_node *parent, const char *name)
+{
+    int len;
+    int nextlevel = 0; /* 0 - no starttag, 1 - '/' starttag, 2 - '=' starttag + '}' endtag */
+
+    if (!name) {
+        /* removing last path segment */
+        if (ctx->path[ctx->path_len - 1] == '}') {
+            for (; ctx->path[ctx->path_len] != '=' && ctx->path[ctx->path_len] != '{'; --ctx->path_len);
+            if (ctx->path[ctx->path_len] == '=') {
+                ctx->path[ctx->path_len++] = '}';
+            } else {
+                /* not a top-level special tag, remove also preceiding '/' */
+                goto remove_nodelevel;
+            }
+        } else {
+remove_nodelevel:
+            for (; ctx->path[ctx->path_len] != '/' ; --ctx->path_len);
+            if (ctx->path_len == 0) {
+                /* top-level (last segment) */
+                ++ctx->path_len;
+            }
+        }
+        /* set new terminating NULL-byte */
+        ctx->path[ctx->path_len] = '\0';
+    } else {
+        if (ctx->path_len > 1) {
+            if (!parent && ctx->path[ctx->path_len - 1] == '}' && ctx->path[ctx->path_len - 2] != '\'') {
+                /* extension of the special tag */
+                nextlevel = 2;
+                --ctx->path_len;
+            } else {
+                /* there is already some path, so add next level */
+                nextlevel = 1;
+            }
+        } /* else the path is just initiated with '/', so do not add additional slash in case of top-level nodes */
+
+        if (nextlevel != 2) {
+            if ((parent && parent->module == ctx->mod) || (!parent && ctx->path_len > 1 && name[0] == '{')) {
+                /* module not changed, print the name unprefixed */
+                len = sprintf(&ctx->path[ctx->path_len], "%s%s", nextlevel ? "/" : "", name);
+            } else {
+                len = sprintf(&ctx->path[ctx->path_len], "%s%s:%s", nextlevel ? "/" : "", ctx->mod->name, name);
+            }
+        } else {
+            len = sprintf(&ctx->path[ctx->path_len], "='%s'}", name);
+        }
+        ctx->path_len += len;
+    }
+}
+
+/**
  * @brief Duplicate the compiled pattern structure.
  *
  * Instead of duplicating memory, the reference counter in the @p orig is increased.
@@ -640,6 +700,8 @@
     unsigned int u;
     LY_ERR ret = LY_SUCCESS;
 
+    lysc_update_path(ctx, NULL, ident_p->name);
+
     COMPILE_CHECK_UNIQUENESS(ctx, idents, name, ident, "identity", ident_p->name);
     DUP_STRING(ctx->ctx, ident_p->name, ident->name);
     DUP_STRING(ctx->ctx, ident_p->dsc, ident->dsc);
@@ -650,6 +712,7 @@
     COMPILE_ARRAY_GOTO(ctx, ident_p->exts, ident->exts, u, lys_compile_ext, ret, done);
     ident->flags = ident_p->flags;
 
+    lysc_update_path(ctx, NULL, NULL);
 done:
     return ret;
 }
@@ -809,19 +872,28 @@
         if (!idents_p[i].bases) {
             continue;
         }
+        lysc_update_path(ctx, NULL, idents[i].name);
         LY_CHECK_RET(lys_compile_identity_bases(ctx, idents_p[i].bases, &idents[i], NULL));
+        lysc_update_path(ctx, NULL, NULL);
     }
     return LY_SUCCESS;
 }
 
 LY_ERR
-lys_feature_precompile(struct ly_ctx *ctx, struct lys_module *module, struct lysp_feature *features_p, struct lysc_feature **features)
+lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module, struct lysp_feature *features_p, struct lysc_feature **features)
 {
     unsigned int offset = 0, u;
     struct lysc_ctx context = {0};
 
-    assert(ctx);
-    context.ctx = ctx;
+    assert(ctx_sc || ctx);
+
+    if (!ctx_sc) {
+        context.ctx = ctx;
+        context.mod = module;
+        context.path_len = 1;
+        context.path[0] = '/';
+        ctx_sc = &context;
+    }
 
     if (!features_p) {
         return LY_SUCCESS;
@@ -830,16 +902,22 @@
         offset = LY_ARRAY_SIZE(*features);
     }
 
-    LY_ARRAY_CREATE_RET(ctx, *features, LY_ARRAY_SIZE(features_p), LY_EMEM);
+    lysc_update_path(ctx_sc, NULL, "{feature}");
+    LY_ARRAY_CREATE_RET(ctx_sc->ctx, *features, LY_ARRAY_SIZE(features_p), LY_EMEM);
     LY_ARRAY_FOR(features_p, u) {
+        lysc_update_path(ctx_sc, NULL, features_p[u].name);
+
         LY_ARRAY_INCREMENT(*features);
-        COMPILE_CHECK_UNIQUENESS(&context, *features, name, &(*features)[offset + u], "feature", features_p[u].name);
-        DUP_STRING(ctx, features_p[u].name, (*features)[offset + u].name);
-        DUP_STRING(ctx, features_p[u].dsc, (*features)[offset + u].dsc);
-        DUP_STRING(ctx, features_p[u].ref, (*features)[offset + u].ref);
+        COMPILE_CHECK_UNIQUENESS(ctx_sc, *features, name, &(*features)[offset + u], "feature", features_p[u].name);
+        DUP_STRING(ctx_sc->ctx, features_p[u].name, (*features)[offset + u].name);
+        DUP_STRING(ctx_sc->ctx, features_p[u].dsc, (*features)[offset + u].dsc);
+        DUP_STRING(ctx_sc->ctx, features_p[u].ref, (*features)[offset + u].ref);
         (*features)[offset + u].flags = features_p[u].flags;
-        (*features)[offset + u].module = module;
+        (*features)[offset + u].module = ctx_sc->mod;
+
+        lysc_update_path(ctx_sc, NULL, NULL);
     }
+    lysc_update_path(ctx_sc, NULL, NULL);
 
     return LY_SUCCESS;
 }
@@ -915,12 +993,15 @@
     struct lysc_feature *feature, **df;
     LY_ERR ret = LY_SUCCESS;
 
+
     /* find the preprecompiled feature */
     LY_ARRAY_FOR(features, x) {
         if (strcmp(features[x].name, feature_p->name)) {
             continue;
         }
         feature = &features[x];
+        lysc_update_path(ctx, NULL, "{feature}");
+        lysc_update_path(ctx, NULL, feature_p->name);
 
         /* finish compilation started in lys_feature_precompile() */
         COMPILE_ARRAY_GOTO(ctx, feature_p->exts, feature->exts, u, lys_compile_ext, ret, done);
@@ -945,6 +1026,8 @@
                 }
             }
         }
+        lysc_update_path(ctx, NULL, NULL);
+        lysc_update_path(ctx, NULL, NULL);
     done:
         return ret;
     }
@@ -2303,11 +2386,17 @@
     int parent_times = 0, has_predicate;
     unsigned int iter, u;
     LY_ERR ret = LY_SUCCESS;
+    char *logpath;
 
     assert(ctx);
     assert(startnode);
     assert(leafref);
 
+    logpath = lysc_path(startnode, LY_PATH_LOG);
+    strncpy(ctx->path, logpath, LYSC_CTX_BUFSIZE - 1);
+    ctx->path_len = strlen(logpath);
+    free(logpath);
+
     iter = 0;
     id = leafref->path;
     while(*id && (ret = lys_path_token(&id, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate)) == LY_SUCCESS) {
@@ -2410,6 +2499,8 @@
         return LY_EVALID;
     }
 
+    ctx->path_len = 1;
+    ctx->path[1] = '\0';
     return LY_SUCCESS;
 }
 
@@ -3148,6 +3239,8 @@
     unsigned int u;
     int opt_prev = ctx->options;
 
+    lysc_update_path(ctx, parent, action_p->name);
+
     if (lys_compile_node_uniqness(ctx, parent ? lysc_node_children(parent, 0) : ctx->mod->compiled->data,
                                   parent ? lysc_node_actions(parent) : ctx->mod->compiled->rpcs,
                                   parent ? lysc_node_notifs(parent) : ctx->mod->compiled->notifs,
@@ -3181,22 +3274,27 @@
     COMPILE_ARRAY_GOTO(ctx, action_p->exts, action->exts, u, lys_compile_ext, ret, cleanup);
 
     /* input */
+    lysc_update_path(ctx, (struct lysc_node*)action, "input");
     COMPILE_ARRAY_GOTO(ctx, action_p->input.musts, action->input.musts, u, lys_compile_must, ret, cleanup);
     COMPILE_ARRAY_GOTO(ctx, action_p->input.exts, action->input_exts, u, lys_compile_ext, ret, cleanup);
     ctx->options |= LYSC_OPT_RPC_INPUT;
     LY_LIST_FOR(action_p->input.data, child_p) {
         LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node*)action, uses_status));
     }
+    lysc_update_path(ctx, NULL, NULL);
     ctx->options = opt_prev;
 
     /* output */
+    lysc_update_path(ctx, (struct lysc_node*)action, "output");
     COMPILE_ARRAY_GOTO(ctx, action_p->output.musts, action->output.musts, u, lys_compile_must, ret, cleanup);
     COMPILE_ARRAY_GOTO(ctx, action_p->output.exts, action->output_exts, u, lys_compile_ext, ret, cleanup);
     ctx->options |= LYSC_OPT_RPC_OUTPUT;
     LY_LIST_FOR(action_p->output.data, child_p) {
         LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node*)action, uses_status));
     }
+    lysc_update_path(ctx, NULL, NULL);
 
+    lysc_update_path(ctx, NULL, NULL);
 cleanup:
     ctx->options = opt_prev;
     return ret;
@@ -3221,6 +3319,8 @@
     unsigned int u;
     int opt_prev = ctx->options;
 
+    lysc_update_path(ctx, parent, notif_p->name);
+
     if (lys_compile_node_uniqness(ctx, parent ? lysc_node_children(parent, 0) : ctx->mod->compiled->data,
                                   parent ? lysc_node_actions(parent) : ctx->mod->compiled->rpcs,
                                   parent ? lysc_node_notifs(parent) : ctx->mod->compiled->notifs,
@@ -3259,6 +3359,7 @@
         LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node*)notif, uses_status));
     }
 
+    lysc_update_path(ctx, NULL, NULL);
 cleanup:
     ctx->options = opt_prev;
     return ret;
@@ -3587,6 +3688,7 @@
                 return LY_EVALID;
             }
         }
+        lysc_update_path(ctx, (struct lysc_node*)list, (*key)->name);
         /* key must have the same config flag as the list itself */
         if ((list->flags & LYS_CONFIG_MASK) != ((*key)->flags & LYS_CONFIG_MASK)) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Key of the configuration list must not be status leaf.");
@@ -3596,19 +3698,19 @@
             /* YANG 1.0 denies key to be of empty type */
             if ((*key)->type->basetype == LY_TYPE_EMPTY) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "Key of a list can be of type \"empty\" only in YANG 1.1 modules.");
+                       "List's key cannot be of \"empty\" type until it is in YANG 1.1 module.");
                 return LY_EVALID;
             }
         } else {
             /* when and if-feature are illegal on list keys */
             if ((*key)->when) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "List's key \"%s\" must not have any \"when\" statement.", (*key)->name);
+                       "List's key must not have any \"when\" statement.");
                 return LY_EVALID;
             }
             if ((*key)->iffeatures) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "List's key \"%s\" must not have any \"if-feature\" statement.", (*key)->name);
+                       "List's key must not have any \"if-feature\" statement.");
                 return LY_EVALID;
             }
         }
@@ -3627,6 +3729,7 @@
 
         /* next key value */
         keystr = delim;
+        lysc_update_path(ctx, NULL, NULL);
     }
 
     /* uniques */
@@ -3697,7 +3800,7 @@
 }
 
 static LY_ERR
-lys_compile_deviation_set_choice_dflt(struct lysc_ctx *ctx, const char *devnodeid, const char *dflt, struct lysc_node_choice *ch)
+lys_compile_deviation_set_choice_dflt(struct lysc_ctx *ctx, const char *dflt, struct lysc_node_choice *ch)
 {
     struct lys_module *mod;
     const char *prefix = NULL, *name;
@@ -3718,9 +3821,8 @@
     if (prefix) {
         if (!(mod = lys_module_find_prefix(ctx->mod, prefix, prefix_len))) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                   "Invalid deviation (%s) adding \"default\" property \"%s\" of choice. "
-                   "The prefix does not match any imported module of the deviation module.",
-                   devnodeid, dflt);
+                   "Invalid deviation adding \"default\" property \"%s\" of choice. "
+                   "The prefix does not match any imported module of the deviation module.", dflt);
             return LY_EVALID;
         }
     } else {
@@ -3730,8 +3832,7 @@
     cs = (struct lysc_node_case*)lys_child((struct lysc_node*)ch, mod, name, 0, LYS_CASE, LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCASE);
     if (!cs) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-               "Invalid deviation (%s) adding \"default\" property \"%s\" of choice - the specified case does not exists.",
-               devnodeid, dflt);
+               "Invalid deviation adding \"default\" property \"%s\" of choice - the specified case does not exists.", dflt);
         return LY_EVALID;
     }
 
@@ -3742,8 +3843,8 @@
         }
         if (node->flags & LYS_MAND_TRUE) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                   "Invalid deviation (%s) adding \"default\" property \"%s\" of choice - "
-                   "mandatory node \"%s\" under the default case.", devnodeid, dflt, node->name);
+                   "Invalid deviation adding \"default\" property \"%s\" of choice - "
+                   "mandatory node \"%s\" under the default case.", dflt, node->name);
             return LY_EVALID;
         }
     }
@@ -3961,7 +4062,6 @@
  * @param[in] ctx Compile context.
  * @param[in] node Target node where the config is supposed to be changed.
  * @param[in] config_flag Node's config flag to be applied to the @p node.
- * @param[in] nodeid Schema nodeid used to identify target of refine/deviation (for logging).
  * @param[in] inheriting Flag (inverted) to check the refined config compatibility with the node's parent. This is
  * done only on the node for which the refine was created. The function applies also recursively to apply the config change
  * to the complete subtree (except the subnodes with explicit config set) and the test is not needed for the subnodes.
@@ -3970,7 +4070,7 @@
  */
 static LY_ERR
 lys_compile_change_config(struct lysc_ctx *ctx, struct lysc_node *node, uint16_t config_flag,
-                          const char *nodeid, int inheriting, int refine_flag)
+                          int inheriting, int refine_flag)
 {
     struct lysc_node *child;
     uint16_t config = config_flag & LYS_CONFIG_MASK;
@@ -3984,8 +4084,8 @@
         /* explicit change */
         if (config == LYS_CONFIG_W && node->parent && (node->parent->flags & LYS_CONFIG_R)) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                   "Invalid %s of config in \"%s\" - configuration node cannot be child of any state data node.",
-                   refine_flag ? "refine" : "deviation", nodeid);
+                   "Invalid %s of config - configuration node cannot be child of any state data node.",
+                   refine_flag ? "refine" : "deviation");
             return LY_EVALID;
         }
         node->flags |= LYS_SET_CONFIG;
@@ -3994,8 +4094,8 @@
             if ((node->flags & LYS_CONFIG_W) && (config == LYS_CONFIG_R)) {
                 /* setting config flags, but have node with explicit config true */
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "Invalid %s of config in \"%s\" - configuration node cannot be child of any state data node.",
-                       refine_flag ? "refine" : "deviation", nodeid);
+                       "Invalid %s of config - configuration node cannot be child of any state data node.",
+                       refine_flag ? "refine" : "deviation");
                 return LY_EVALID;
             }
             /* do not change config on nodes where the config is explicitely set, this does not apply to
@@ -4008,7 +4108,7 @@
 
     /* inherit the change into the children */
     LY_LIST_FOR((struct lysc_node*)lysc_node_children(node, 0), child) {
-        LY_CHECK_RET(lys_compile_change_config(ctx, child, config_flag, nodeid, 1, refine_flag));
+        LY_CHECK_RET(lys_compile_change_config(ctx, child, config_flag, 1, refine_flag));
     }
 
     return LY_SUCCESS;
@@ -4155,6 +4255,9 @@
     unsigned int u;
     int opt_prev = ctx->options;
 
+    lysc_update_path(ctx, NULL, "{augment}");
+    lysc_update_path(ctx, NULL, aug_p->nodeid);
+
     ret = lys_resolve_schema_nodeid(ctx, aug_p->nodeid, 0, parent, parent ? parent->module : ctx->mod_def,
                                                LYS_CONTAINER | LYS_LIST | LYS_CHOICE | LYS_CASE | LYS_INOUT | LYS_NOTIF,
                                                1, (const struct lysc_node**)&target, &flags);
@@ -4183,8 +4286,8 @@
                 && !(target->nodetype != LYS_CHOICE && node_p->nodetype == LYS_USES)
                 && !(node_p->nodetype & (LYS_ANYDATA | LYS_CONTAINER | LYS_CHOICE | LYS_LEAF | LYS_LIST | LYS_LEAFLIST))) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                   "Invalid augment (%s) of %s node which is not allowed to contain %s node \"%s\".",
-                   aug_p->nodeid, lys_nodetype2str(target->nodetype), lys_nodetype2str(node_p->nodetype), node_p->name);
+                   "Invalid augment of %s node which is not allowed to contain %s node \"%s\".",
+                   lys_nodetype2str(target->nodetype), lys_nodetype2str(node_p->nodetype), node_p->name);
             return LY_EVALID;
         }
 
@@ -4216,8 +4319,7 @@
             node->flags &= ~LYS_MAND_TRUE;
             lys_compile_mandatory_parents(target, 0);
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                   "Invalid augment (%s) adding mandatory node \"%s\" without making it conditional via when statement.",
-                   aug_p->nodeid, node->name);
+                   "Invalid augment adding mandatory node \"%s\" without making it conditional via when statement.", node->name);
             return LY_EVALID;
         }
 
@@ -4254,18 +4356,20 @@
         ctx->options = opt_prev;
         if (aug_p->actions) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                   "Invalid augment (%s) of %s node which is not allowed to contain RPC/action node \"%s\".",
-                   aug_p->nodeid, lys_nodetype2str(target->nodetype), aug_p->actions[0].name);
+                   "Invalid augment of %s node which is not allowed to contain RPC/action node \"%s\".",
+                   lys_nodetype2str(target->nodetype), aug_p->actions[0].name);
             return LY_EVALID;
         }
         if (aug_p->notifs) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                   "Invalid augment (%s) of %s node which is not allowed to contain Notification node \"%s\".",
-                   aug_p->nodeid, lys_nodetype2str(target->nodetype), aug_p->notifs[0].name);
+                   "Invalid augment of %s node which is not allowed to contain Notification node \"%s\".",
+                   lys_nodetype2str(target->nodetype), aug_p->notifs[0].name);
             return LY_EVALID;
         }
     }
 
+    lysc_update_path(ctx, NULL, NULL);
+    lysc_update_path(ctx, NULL, NULL);
 error:
     ctx->options = opt_prev;
     return ret;
@@ -4277,7 +4381,6 @@
  * @param[in] ctx Compile context.
  * @param[in] node Target node where the mandatory property is supposed to be changed.
  * @param[in] mandatory_flag Node's mandatory flag to be applied to the @p node.
- * @param[in] nodeid Schema nodeid used to identify target of refine/deviation (for logging).
  * @param[in] refine_flag Flag to distinguish if the change is caused by refine (flag set) or deviation (for logging).
  * @param[in] It is also used as a flag for testing for compatibility with default statement. In case of deviations,
  * there can be some other deviations of the default properties that we are testing here. To avoid false positive failure,
@@ -4285,12 +4388,12 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lys_compile_change_mandatory(struct lysc_ctx *ctx, struct lysc_node *node, uint16_t mandatory_flag, const char *nodeid, int refine_flag)
+lys_compile_change_mandatory(struct lysc_ctx *ctx, struct lysc_node *node, uint16_t mandatory_flag, int refine_flag)
 {
     if (!(node->nodetype & (LYS_LEAF | LYS_ANYDATA | LYS_ANYXML | LYS_CHOICE))) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-               "Invalid %s of mandatory in \"%s\" - %s cannot hold mandatory statement.",
-               refine_flag ? "refine" : "deviation", nodeid, lys_nodetype2str(node->nodetype));
+               "Invalid %s of mandatory - %s cannot hold mandatory statement.",
+               refine_flag ? "refine" : "deviation", lys_nodetype2str(node->nodetype));
         return LY_EVALID;
     }
 
@@ -4300,7 +4403,7 @@
             if (node->flags & LYS_SET_DFLT) {
                 if (refine_flag) {
                     LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                           "Invalid refine of mandatory in \"%s\" - leaf already has \"default\" statement.", nodeid);
+                           "Invalid refine of mandatory - leaf already has \"default\" statement.");
                     return LY_EVALID;
                 }
             } else {
@@ -4311,13 +4414,12 @@
         } else if ((node->nodetype & LYS_CHOICE) && ((struct lysc_node_choice*)node)->dflt) {
             if (refine_flag) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "Invalid refine of mandatory in \"%s\" - choice already has \"default\" statement.", nodeid);
+                       "Invalid refine of mandatory - choice already has \"default\" statement.");
                 return LY_EVALID;
             }
         }
         if (refine_flag && node->parent && (node->parent->flags & LYS_SET_DFLT)) {
-            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                   "Invalid refine of mandatory in \"%s\" under the default case.", nodeid);
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Invalid refine of mandatory under the default case.");
             return LY_EVALID;
         }
 
@@ -4386,7 +4488,7 @@
         mod = lys_module_find_prefix(ctx->mod_def, prefix, prefix_len);
         if (!mod) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                   "Invalid prefix used for grouping reference (%s).", uses_p->name);
+                   "Invalid prefix used for grouping reference.", uses_p->name);
             return LY_EVALID;
         }
     } else {
@@ -4534,9 +4636,12 @@
 
     /* reload previous context's mod_def */
     ctx->mod_def = mod_old;
+    lysc_update_path(ctx, NULL, "{refine}");
 
     /* apply refine */
     LY_ARRAY_FOR(uses_p->refines, struct lysp_refine, rfn) {
+        lysc_update_path(ctx, NULL, rfn->nodeid);
+
         LY_CHECK_GOTO(lys_resolve_schema_nodeid(ctx, rfn->nodeid, 0, (struct lysc_node*)&context_node_fake, ctx->mod,
                                                 0, 0, (const struct lysc_node**)&node, &flags),
                       cleanup);
@@ -4546,14 +4651,14 @@
         if (rfn->dflts) {
             if ((node->nodetype != LYS_LEAFLIST) && LY_ARRAY_SIZE(rfn->dflts) > 1) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "Invalid refine of default in \"%s\" - %s cannot hold %d default values.",
-                       rfn->nodeid, lys_nodetype2str(node->nodetype), LY_ARRAY_SIZE(rfn->dflts));
+                       "Invalid refine of default - %s cannot hold %d default values.",
+                       lys_nodetype2str(node->nodetype), LY_ARRAY_SIZE(rfn->dflts));
                 goto cleanup;
             }
             if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_CHOICE))) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "Invalid refine of default in \"%s\" - %s cannot hold default value(s).",
-                       rfn->nodeid, lys_nodetype2str(node->nodetype));
+                       "Invalid refine of default - %s cannot hold default value(s).",
+                       lys_nodetype2str(node->nodetype));
                 goto cleanup;
             }
             if (node->nodetype == LYS_LEAF) {
@@ -4602,7 +4707,7 @@
         /* config */
         if (rfn->flags & LYS_CONFIG_MASK) {
             if (!flags) {
-                LY_CHECK_GOTO(lys_compile_change_config(ctx, node, rfn->flags, rfn->nodeid, 0, 1), cleanup);
+                LY_CHECK_GOTO(lys_compile_change_config(ctx, node, rfn->flags, 0, 1), cleanup);
             } else {
                 LOGWRN(ctx->ctx, "Refining config inside %s has no effect (%s).",
                        flags & LYSC_OPT_NOTIFICATION ? "Notification" : "RPC/action", ctx->path);
@@ -4611,15 +4716,15 @@
 
         /* mandatory */
         if (rfn->flags & LYS_MAND_MASK) {
-            LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, node, rfn->flags, rfn->nodeid, 1), cleanup);
+            LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, node, rfn->flags, 1), cleanup);
         }
 
         /* presence */
         if (rfn->presence) {
             if (node->nodetype != LYS_CONTAINER) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "Invalid refine of presence statement in \"%s\" - %s cannot hold the presence statement.",
-                       rfn->nodeid, lys_nodetype2str(node->nodetype));
+                       "Invalid refine of presence statement - %s cannot hold the presence statement.",
+                       lys_nodetype2str(node->nodetype));
                 goto cleanup;
             }
             node->flags |= LYS_PRESENCE;
@@ -4646,8 +4751,8 @@
                 break;
             default:
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "Invalid refine of must statement in \"%s\" - %s cannot hold any must statement.",
-                       rfn->nodeid, lys_nodetype2str(node->nodetype));
+                       "Invalid refine of must statement - %s cannot hold any must statement.",
+                       lys_nodetype2str(node->nodetype));
                 goto cleanup;
             }
         }
@@ -4687,8 +4792,8 @@
                 break;
             default:
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "Invalid refine of %s statement in \"%s\" - %s cannot hold this statement.",
-                       (rfn->flags & LYS_SET_MAX) ? "max-elements" : "min-elements", rfn->nodeid, lys_nodetype2str(node->nodetype));
+                       "Invalid refine of %s statement - %s cannot hold this statement.",
+                       (rfn->flags & LYS_SET_MAX) ? "max-elements" : "min-elements", lys_nodetype2str(node->nodetype));
                 goto cleanup;
             }
         }
@@ -4698,19 +4803,22 @@
             /* any node in compiled tree can get additional if-feature, so do not check nodetype */
             COMPILE_ARRAY_GOTO(ctx, rfn->iffeatures, node->iffeatures, u, lys_compile_iffeature, ret, cleanup);
         }
+
+        lysc_update_path(ctx, NULL, NULL);
     }
 
     /* do some additional checks of the changed nodes when all the refines are applied */
     for (u = 0; u < refined.count; ++u) {
         node = (struct lysc_node*)refined.objs[u];
         rfn = &uses_p->refines[u];
+        lysc_update_path(ctx, NULL, rfn->nodeid);
 
         /* check possible conflict with default value (default added, mandatory left true) */
         if ((node->flags & LYS_MAND_TRUE) &&
                 (((node->nodetype & LYS_CHOICE) && ((struct lysc_node_choice*)node)->dflt) ||
                 ((node->nodetype & LYS_LEAF) && (node->flags & LYS_SET_DFLT)))) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                   "Invalid refine of default in \"%s\" - the node is mandatory.", rfn->nodeid);
+                   "Invalid refine of default - the node is mandatory.");
             goto cleanup;
         }
 
@@ -4724,13 +4832,14 @@
             }
             if (min > max) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "Invalid refine of %s statement in \"%s\" - \"min-elements\" is bigger than \"max-elements\".",
-                       (rfn->flags & LYS_SET_MAX) ? "max-elements" : "min-elements", rfn->nodeid);
+                       "Invalid refine of %s statement - \"min-elements\" is bigger than \"max-elements\".",
+                       (rfn->flags & LYS_SET_MAX) ? "max-elements" : "min-elements");
                 goto cleanup;
             }
         }
     }
 
+    lysc_update_path(ctx, NULL, NULL);
     ret = LY_SUCCESS;
 
 cleanup:
@@ -4765,6 +4874,53 @@
     return ret;
 }
 
+static int
+lys_compile_grouping_pathlog(struct lysc_ctx *ctx, struct lysp_node *node, char **path)
+{
+    struct lysp_node *iter;
+    int len = 0;
+
+    *path = NULL;
+    for (iter = node; iter && len >= 0; iter = iter->parent) {
+        char *s = *path;
+        char *id;
+
+        switch (iter->nodetype) {
+        case LYS_USES:
+            asprintf(&id, "{uses='%s'}", iter->name);
+            break;
+        case LYS_GROUPING:
+            asprintf(&id, "{grouping='%s'}", iter->name);
+            break;
+        case LYS_AUGMENT:
+            asprintf(&id, "{augment='%s'}", iter->name);
+            break;
+        default:
+            id = strdup(iter->name);
+            break;
+        }
+
+        if (!iter->parent) {
+            /* print prefix */
+            len = asprintf(path, "/%s:%s%s", ctx->mod->name, id, s ? s : "");
+        } else {
+            /* prefix is the same as in parent */
+            len = asprintf(path, "/%s%s", id, s ? s : "");
+        }
+        free(s);
+        free(id);
+    }
+
+    if (len < 0) {
+        free(*path);
+        *path = NULL;
+    } else if (len == 0) {
+        *path = strdup("/");
+        len = 1;
+    }
+    return len;
+}
+
 /**
  * @brief Validate groupings that were defined but not directly used in the schema itself.
  *
@@ -4775,6 +4931,9 @@
 lys_compile_grouping(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysp_grp *grp)
 {
     LY_ERR ret;
+    char *path;
+    int len;
+
     struct lysp_node_uses fake_uses = {
         .parent = node_p,
         .nodetype = LYS_USES,
@@ -4797,7 +4956,24 @@
     if (grp->parent) {
         LOGWRN(ctx->ctx, "Locally scoped grouping \"%s\" not used.", grp->name);
     }
+
+    len = lys_compile_grouping_pathlog(ctx, grp->parent, &path);
+    if (len < 0) {
+        LOGMEM(ctx->ctx);
+        return LY_EMEM;
+    }
+    strncpy(ctx->path, path, LYSC_CTX_BUFSIZE - 1);
+    ctx->path_len = (uint16_t)len;
+    free(path);
+
+    lysc_update_path(ctx, NULL, "{grouping}");
+    lysc_update_path(ctx, NULL, grp->name);
     ret = lys_compile_uses(ctx, &fake_uses, (struct lysc_node*)&fake_container);
+    lysc_update_path(ctx, NULL, NULL);
+    lysc_update_path(ctx, NULL, NULL);
+
+    ctx->path_len = 1;
+    ctx->path[1] = '\0';
 
     /* cleanup */
     lysc_node_container_free(ctx->ctx, &fake_container);
@@ -4826,6 +5002,13 @@
     unsigned int u;
     LY_ERR (*node_compile_spec)(struct lysc_ctx*, struct lysp_node*, struct lysc_node*);
 
+    if (node_p->nodetype != LYS_USES) {
+        lysc_update_path(ctx, parent, node_p->name);
+    } else {
+        lysc_update_path(ctx, NULL, "{uses}");
+        lysc_update_path(ctx, NULL, node_p->name);
+    }
+
     switch (node_p->nodetype) {
     case LYS_CONTAINER:
         node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_container));
@@ -4853,7 +5036,10 @@
         node_compile_spec = lys_compile_node_any;
         break;
     case LYS_USES:
-        return lys_compile_uses(ctx, (struct lysp_node_uses*)node_p, parent);
+        ret = lys_compile_uses(ctx, (struct lysp_node_uses*)node_p, parent);
+        lysc_update_path(ctx, NULL, NULL);
+        lysc_update_path(ctx, NULL, NULL);
+        return ret;
     default:
         LOGINT(ctx->ctx);
         return LY_EINT;
@@ -4936,9 +5122,16 @@
         lys_compile_mandatory_parents(parent, 1);
     }
 
+    lysc_update_path(ctx, NULL, NULL);
+
     /* insert into parent's children */
     if (parent) {
         if (parent->nodetype == LYS_CHOICE) {
+            if (node_p->parent->nodetype == LYS_CASE) {
+                lysc_update_path(ctx, parent, node_p->parent->name);
+            } else {
+                lysc_update_path(ctx, parent, node->name);
+            }
             cs = lys_compile_node_case(ctx, node_p->parent, (struct lysc_node_choice*)parent, node);
             LY_CHECK_ERR_GOTO(!cs, ret = LY_EVALID, error);
             if (uses_status) {
@@ -4949,10 +5142,16 @@
              * uses_status cannot be applied here since uses cannot be child statement of choice */
             LY_CHECK_GOTO(lys_compile_status(ctx, &node->flags, cs->flags), error);
             node->parent = (struct lysc_node*)cs;
+            lysc_update_path(ctx, parent, node->name);
         } else { /* other than choice */
+            lysc_update_path(ctx, parent, node->name);
             node->parent = parent;
         }
         LY_CHECK_RET(lys_compile_node_connect(ctx, parent->nodetype == LYS_CASE ? parent->parent : parent, node), LY_EVALID);
+
+        if (parent->nodetype == LYS_CHOICE) {
+            lysc_update_path(ctx, NULL, NULL);
+        }
     } else {
         /* top-level element */
         if (!ctx->mod->compiled->data) {
@@ -4963,11 +5162,13 @@
             node->prev = ctx->mod->compiled->data->prev;
             ctx->mod->compiled->data->prev = node;
         }
+        lysc_update_path(ctx, parent, node->name);
         if (lys_compile_node_uniqness(ctx, ctx->mod->compiled->data, ctx->mod->compiled->rpcs,
                                       ctx->mod->compiled->notifs, node->name, node)) {
             return LY_EVALID;
         }
     }
+    lysc_update_path(ctx, NULL, NULL);
 
     return LY_SUCCESS;
 
@@ -5116,10 +5317,13 @@
         return LY_SUCCESS;
     }
 
+    lysc_update_path(ctx, NULL, "{deviation}");
+
     /* ... and group them by the target node */
     devs = calloc(devs_p.count, sizeof *devs);
     for (u = 0; u < devs_p.count; ++u) {
         dev = devs_p.objs[u];
+        lysc_update_path(ctx, NULL, dev->nodeid);
 
         /* resolve the target */
         LY_CHECK_GOTO(lys_resolve_schema_nodeid(ctx, dev->nodeid, 0, NULL, ctx->mod, 0, 1,
@@ -5154,49 +5358,50 @@
                 devs[i]->not_supported = 1;
             }
         }
+
+        lysc_update_path(ctx, NULL, NULL);
     }
 
     /* MACROS for deviates checking */
 #define DEV_CHECK_NODETYPE(NODETYPES, DEVTYPE, PROPERTY) \
     if (!(devs[u]->target->nodetype & (NODETYPES))) { \
-        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype), DEVTYPE, PROPERTY);\
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, lys_nodetype2str(devs[u]->target->nodetype), DEVTYPE, PROPERTY);\
         goto cleanup; \
     }
 
 #define DEV_CHECK_CARDINALITY(ARRAY, MAX, PROPERTY) \
     if (LY_ARRAY_SIZE(ARRAY) > MAX) { \
-        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Invalid deviation (%s) of %s with too many (%u) %s properties.", \
-               devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype), LY_ARRAY_SIZE(ARRAY), PROPERTY); \
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Invalid deviation of %s with too many (%u) %s properties.", \
+               lys_nodetype2str(devs[u]->target->nodetype), LY_ARRAY_SIZE(ARRAY), PROPERTY); \
         goto cleanup; \
     }
 
 #define DEV_CHECK_NONPRESENCE(TYPE, COND, MEMBER, PROPERTY, VALUEMEMBER) \
     if (((TYPE)devs[u]->target)->MEMBER && (COND)) { \
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, \
-               "Invalid deviation (%s) adding \"%s\" property which already exists (with value \"%s\").", \
-               devs[u]->nodeid, PROPERTY, ((TYPE)devs[u]->target)->VALUEMEMBER); \
+               "Invalid deviation adding \"%s\" property which already exists (with value \"%s\").", \
+               PROPERTY, ((TYPE)devs[u]->target)->VALUEMEMBER); \
         goto cleanup; \
     }
 
 #define DEV_CHECK_NONPRESENCE_UINT(TYPE, COND, MEMBER, PROPERTY) \
     if (((TYPE)devs[u]->target)->MEMBER COND) { \
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, \
-               "Invalid deviation (%s) adding \"%s\" property which already exists (with value \"%u\").", \
-               devs[u]->nodeid, PROPERTY, ((TYPE)devs[u]->target)->MEMBER); \
+               "Invalid deviation adding \"%s\" property which already exists (with value \"%u\").", \
+               PROPERTY, ((TYPE)devs[u]->target)->MEMBER); \
         goto cleanup; \
     }
 
 #define DEV_CHECK_PRESENCE(TYPE, COND, MEMBER, DEVTYPE, PROPERTY, VALUE) \
     if (!((TYPE)devs[u]->target)->MEMBER || COND) { \
-        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NOT_PRESENT, devs[u]->nodeid, DEVTYPE, PROPERTY, VALUE); \
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NOT_PRESENT, DEVTYPE, PROPERTY, VALUE); \
         goto cleanup; \
     }
 
 #define DEV_CHECK_PRESENCE_UINT(TYPE, COND, MEMBER, PROPERTY) \
     if (!(((TYPE)devs[u]->target)->MEMBER COND)) { \
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, \
-               "Invalid deviation (%s) replacing with \"%s\" property \"%u\" which is not present.", \
-               devs[u]->nodeid, PROPERTY, d_rpl->MEMBER); \
+               "Invalid deviation replacing with \"%s\" property \"%u\" which is not present.", PROPERTY, d_rpl->MEMBER); \
         goto cleanup; \
     }
 
@@ -5204,8 +5409,8 @@
     DEV_CHECK_PRESENCE(TYPE, 0, MEMBER_TRG, "deleting", PROPERTY, d_del->MEMBER_DEV); \
     if (strcmp(((TYPE)devs[u]->target)->MEMBER_TRG, d_del->MEMBER_DEV)) { \
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, \
-               "Invalid deviation (%s) deleting \"%s\" property \"%s\" which does not match the target's property value \"%s\".", \
-               devs[u]->nodeid, PROPERTY, d_del->MEMBER_DEV, ((TYPE)devs[u]->target)->MEMBER_TRG); \
+               "Invalid deviation deleting \"%s\" property \"%s\" which does not match the target's property value \"%s\".", \
+               PROPERTY, d_del->MEMBER_DEV, ((TYPE)devs[u]->target)->MEMBER_TRG); \
         goto cleanup; \
     } \
     DELFUNC(ctx->ctx, ((TYPE)devs[u]->target)->MEMBER_TRG); \
@@ -5219,8 +5424,8 @@
         } \
         if (y == LY_ARRAY_SIZE(((TYPE)devs[u]->target)->ARRAY_TRG)) { \
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, \
-                   "Invalid deviation (%s) deleting \"%s\" property \"%s\" which does not match any of the target's property values.", \
-                   devs[u]->nodeid, PROPERTY, d_del->ARRAY_DEV[x]VALMEMBER); \
+                   "Invalid deviation deleting \"%s\" property \"%s\" which does not match any of the target's property values.", \
+                   PROPERTY, d_del->ARRAY_DEV[x]VALMEMBER); \
             goto cleanup; \
         } \
         LY_ARRAY_DECREMENT(((TYPE)devs[u]->target)->ARRAY_TRG); \
@@ -5236,6 +5441,8 @@
 
     /* apply deviations */
     for (u = 0; u < devs_p.count && devs[u]; ++u) {
+        lysc_update_path(ctx, NULL, devs[u]->nodeid);
+
         if (devs[u]->flags & LYSC_OPT_INTERNAL) {
             /* fix the target pointer in case of RPC's/action's input/output */
             if (devs[u]->flags & LYSC_OPT_RPC_INPUT) {
@@ -5343,7 +5550,7 @@
                         /* fall through */
                     default:
                         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
-                               devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype), "add", "must");
+                               lys_nodetype2str(devs[u]->target->nodetype), "add", "must");
                         goto cleanup;
                     }
                 }
@@ -5394,14 +5601,13 @@
                         DEV_CHECK_NONPRESENCE(struct lysc_node_choice*, 1, dflt, "default", dflt->name);
                         /* in contrast to delete, here we strictly resolve the prefix in the module of the deviation
                          * to allow making the default case even the augmented case from the deviating module */
-                        if (lys_compile_deviation_set_choice_dflt(ctx, devs[u]->nodeid, d_add->dflts[0],
-                                                                  (struct lysc_node_choice*)devs[u]->target)) {
+                        if (lys_compile_deviation_set_choice_dflt(ctx, d_add->dflts[0], (struct lysc_node_choice*)devs[u]->target)) {
                             goto cleanup;
                         }
                         break;
                     default:
                         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
-                               devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype), "add", "default");
+                               lys_nodetype2str(devs[u]->target->nodetype), "add", "default");
                         goto cleanup;
                     }
                 }
@@ -5409,32 +5615,32 @@
                 /* [config-stmt] */
                 if (d_add->flags & LYS_CONFIG_MASK) {
                     if (devs[u]->target->nodetype & (LYS_CASE | LYS_INOUT | LYS_ACTION | LYS_NOTIF)) {
-                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid,
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
                                lys_nodetype2str(devs[u]->target->nodetype), "add", "config");
                         goto cleanup;
                     }
                     if (devs[u]->flags) {
-                        LOGWRN(ctx->ctx, "Deviating config inside %s has no effect (%s).",
-                               devs[u]->flags & LYSC_OPT_NOTIFICATION ? "Notification" : "RPC/action", devs[u]->nodeid);
+                        LOGWRN(ctx->ctx, "Deviating config inside %s has no effect.",
+                               devs[u]->flags & LYSC_OPT_NOTIFICATION ? "Notification" : "RPC/action");
                     }
                     if (devs[u]->target->flags & LYS_SET_CONFIG) {
                         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                               "Invalid deviation (%s) adding \"config\" property which already exists (with value \"config %s\").",
-                               devs[u]->nodeid, devs[u]->target->flags & LYS_CONFIG_W ? "true" : "false");
+                               "Invalid deviation adding \"config\" property which already exists (with value \"config %s\").",
+                               devs[u]->target->flags & LYS_CONFIG_W ? "true" : "false");
                         goto cleanup;
                     }
-                    LY_CHECK_GOTO(lys_compile_change_config(ctx, devs[u]->target, d_add->flags, devs[u]->nodeid, 0, 0), cleanup);
+                    LY_CHECK_GOTO(lys_compile_change_config(ctx, devs[u]->target, d_add->flags, 0, 0), cleanup);
                 }
 
                 /* [mandatory-stmt] */
                 if (d_add->flags & LYS_MAND_MASK) {
                     if (devs[u]->target->flags & LYS_MAND_MASK) {
                         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                               "Invalid deviation (%s) adding \"mandatory\" property which already exists (with value \"mandatory %s\").",
-                               devs[u]->nodeid, devs[u]->target->flags & LYS_MAND_TRUE ? "true" : "false");
+                               "Invalid deviation adding \"mandatory\" property which already exists (with value \"mandatory %s\").",
+                               devs[u]->target->flags & LYS_MAND_TRUE ? "true" : "false");
                         goto cleanup;
                     }
-                    LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, devs[u]->target, d_add->flags, devs[u]->nodeid, 0), cleanup);
+                    LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, devs[u]->target, d_add->flags, 0), cleanup);
                 }
 
                 /* [min-elements-stmt] */
@@ -5448,7 +5654,7 @@
                         /* change value */
                         ((struct lysc_node_list*)devs[u]->target)->min = d_add->min;
                     } else {
-                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid,
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
                                lys_nodetype2str(devs[u]->target->nodetype), "add", "min-elements");
                         goto cleanup;
                     }
@@ -5468,7 +5674,7 @@
                         /* change value */
                         ((struct lysc_node_list*)devs[u]->target)->max = d_add->max ? d_add->max : (uint32_t)-1;
                     } else {
-                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid,
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
                                lys_nodetype2str(devs[u]->target->nodetype), "add", "max-elements");
                         goto cleanup;
                     }
@@ -5510,7 +5716,7 @@
                         /* fall through */
                     default:
                         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
-                               devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype), "delete", "must");
+                               lys_nodetype2str(devs[u]->target->nodetype), "delete", "must");
                         goto cleanup;
                     }
                 }
@@ -5548,8 +5754,8 @@
                         }
                         if (!list->uniques || z == LY_ARRAY_SIZE(list->uniques)) {
                             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                                   "Invalid deviation (%s) deleting \"unique\" property \"%s\" which does not match any of the target's property values.",
-                                   devs[u]->nodeid, d_del->uniques[x]);
+                                   "Invalid deviation deleting \"unique\" property \"%s\" which does not match any of the target's property values.",
+                                   d_del->uniques[x]);
                             goto cleanup;
                         }
                     }
@@ -5585,16 +5791,14 @@
                             /* use module prefixes from the deviation module to match the module of the default case */
                             if (!(mod = lys_module_find_prefix(ctx->mod, prefix, prefix_len))) {
                                 LOGVAL(ctx->ctx,LY_VLOG_STR,ctx->path,LYVE_REFERENCE,
-                                       "Invalid deviation (%s) deleting \"default\" property \"%s\" of choice. "
-                                       "The prefix does not match any imported module of the deviation module.",
-                                       devs[u]->nodeid, d_del->dflts[0]);
+                                       "Invalid deviation deleting \"default\" property \"%s\" of choice. "
+                                       "The prefix does not match any imported module of the deviation module.", d_del->dflts[0]);
                                 goto cleanup;
                             }
                             if (mod != ((struct lysc_node_choice*)devs[u]->target)->dflt->module) {
                                 LOGVAL(ctx->ctx,LY_VLOG_STR,ctx->path,LYVE_REFERENCE,
-                                       "Invalid deviation (%s) deleting \"default\" property \"%s\" of choice. "
-                                       "The prefix does not match the default case's module.",
-                                       devs[u]->nodeid, d_del->dflts[0]);
+                                       "Invalid deviation deleting \"default\" property \"%s\" of choice. "
+                                       "The prefix does not match the default case's module.", d_del->dflts[0]);
                                 goto cleanup;
                             }
                         }
@@ -5604,8 +5808,8 @@
                          * the module of the default case, just matching its name */
                         if (strcmp(name, ((struct lysc_node_choice*)devs[u]->target)->dflt->name)) {
                             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                                   "Invalid deviation (%s) deleting \"default\" property \"%s\" of choice does not match the default case name \"%s\".",
-                                   devs[u]->nodeid, d_del->dflts[0], ((struct lysc_node_choice*)devs[u]->target)->dflt->name);
+                                   "Invalid deviation deleting \"default\" property \"%s\" of choice does not match the default case name \"%s\".",
+                                   d_del->dflts[0], ((struct lysc_node_choice*)devs[u]->target)->dflt->name);
                             goto cleanup;
                         }
                         ((struct lysc_node_choice*)devs[u]->target)->dflt->flags &= ~LYS_SET_DFLT;
@@ -5613,7 +5817,7 @@
                         break;
                     default:
                         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
-                               devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype), "delete", "default");
+                               lys_nodetype2str(devs[u]->target->nodetype), "delete", "default");
                         goto cleanup;
                     }
                 }
@@ -5652,14 +5856,13 @@
                         break;
                     case LYS_CHOICE:
                         DEV_CHECK_PRESENCE(struct lysc_node_choice*, 0, dflt, "replacing", "default", d_rpl->dflt);
-                        if (lys_compile_deviation_set_choice_dflt(ctx, devs[u]->nodeid, d_rpl->dflt,
-                                                                  (struct lysc_node_choice*)devs[u]->target)) {
+                        if (lys_compile_deviation_set_choice_dflt(ctx, d_rpl->dflt, (struct lysc_node_choice*)devs[u]->target)) {
                             goto cleanup;
                         }
                         break;
                     default:
                         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
-                               devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype), "replace", "default");
+                               lys_nodetype2str(devs[u]->target->nodetype), "replace", "default");
                         goto cleanup;
                     }
                 }
@@ -5667,26 +5870,26 @@
                 /* [config-stmt] */
                 if (d_rpl->flags & LYS_CONFIG_MASK) {
                     if (devs[u]->target->nodetype & (LYS_CASE | LYS_INOUT | LYS_ACTION | LYS_NOTIF)) {
-                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid,
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
                                lys_nodetype2str(devs[u]->target->nodetype), "replace", "config");
                         goto cleanup;
                     }
                     if (!(devs[u]->target->flags & LYS_SET_CONFIG)) {
-                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NOT_PRESENT, devs[u]->nodeid,
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NOT_PRESENT,
                                "replacing", "config", d_rpl->flags & LYS_CONFIG_W ? "config true" : "config false");
                         goto cleanup;
                     }
-                    LY_CHECK_GOTO(lys_compile_change_config(ctx, devs[u]->target, d_rpl->flags, devs[u]->nodeid, 0, 0), cleanup);
+                    LY_CHECK_GOTO(lys_compile_change_config(ctx, devs[u]->target, d_rpl->flags, 0, 0), cleanup);
                 }
 
                 /* [mandatory-stmt] */
                 if (d_rpl->flags & LYS_MAND_MASK) {
                     if (!(devs[u]->target->flags & LYS_MAND_MASK)) {
-                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NOT_PRESENT, devs[u]->nodeid,
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NOT_PRESENT,
                                "replacing", "mandatory", d_rpl->flags & LYS_MAND_TRUE ? "mandatory true" : "mandatory false");
                         goto cleanup;
                     }
-                    LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, devs[u]->target, d_rpl->flags, devs[u]->nodeid, 0), cleanup);
+                    LY_CHECK_GOTO(lys_compile_change_mandatory(ctx, devs[u]->target, d_rpl->flags, 0), cleanup);
                 }
 
                 /* [min-elements-stmt] */
@@ -5700,7 +5903,7 @@
                         /* change value */
                         ((struct lysc_node_list*)devs[u]->target)->min = d_rpl->min;
                     } else {
-                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid,
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
                                lys_nodetype2str(devs[u]->target->nodetype), "replace", "min-elements");
                         goto cleanup;
                     }
@@ -5720,7 +5923,7 @@
                         /* change value */
                         ((struct lysc_node_list*)devs[u]->target)->max = d_rpl->max ? d_rpl->max : (uint32_t)-1;
                     } else {
-                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE, devs[u]->nodeid,
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DEV_NODETYPE,
                                lys_nodetype2str(devs[u]->target->nodetype), "replace", "max-elements");
                         goto cleanup;
                     }
@@ -5747,8 +5950,7 @@
         }
         if (min > max) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Invalid combination of min-elements and max-elements "
-                   "after deviation (%s): min value %u is bigger than max value %u.",
-                   devs[u]->nodeid, min, max);
+                   "after deviation: min value %u is bigger than max value %u.", min, max);
             goto cleanup;
         }
 
@@ -5757,27 +5959,28 @@
                 && (devs[u]->target->flags & LYS_SET_DFLT)
                 && (devs[u]->target->flags & LYS_MAND_TRUE)) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                   "Invalid deviation (%s) combining default value and mandatory %s.",
-                   devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype));
+                   "Invalid deviation combining default value and mandatory %s.", lys_nodetype2str(devs[u]->target->nodetype));
             goto cleanup;
         } else if ((devs[u]->target->nodetype & LYS_CHOICE)
                 && ((struct lysc_node_choice*)devs[u]->target)->dflt
                 && (devs[u]->target->flags & LYS_MAND_TRUE)) {
-            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                   "Invalid deviation (%s) combining default case and mandatory choice.", devs[u]->nodeid);
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Invalid deviation combining default case and mandatory choice.");
             goto cleanup;
         }
         if (devs[u]->target->parent && (devs[u]->target->parent->flags & LYS_SET_DFLT) && (devs[u]->target->flags & LYS_MAND_TRUE)) {
             /* mandatory node under a default case */
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                   "Invalid deviation (%s) combining mandatory %s \"%s\" in a default choice's case \"%s\".",
-                   devs[u]->nodeid, lys_nodetype2str(devs[u]->target->nodetype), devs[u]->target->name, devs[u]->target->parent->name);
+                   "Invalid deviation combining mandatory %s \"%s\" in a default choice's case \"%s\".",
+                   lys_nodetype2str(devs[u]->target->nodetype), devs[u]->target->name, devs[u]->target->parent->name);
             goto cleanup;
         }
 
         /* TODO check default value(s) according to the type */
+
+        lysc_update_path(ctx, NULL, NULL);
     }
 
+    lysc_update_path(ctx, NULL, NULL);
     ret = LY_SUCCESS;
 
 cleanup:
@@ -5811,12 +6014,14 @@
         /* features are compiled directly into the compiled module structure,
          * but it must be done in two steps to allow forward references (via if-feature) between the features themselves.
          * The features compilation is finished in the main module (lys_compile()). */
-        ret = lys_feature_precompile(ctx->ctx, ctx->mod, submod->features,
+        ret = lys_feature_precompile(ctx, NULL, NULL, submod->features,
                                      mainmod->mod->off_features ? &mainmod->mod->off_features : &mainmod->features);
         LY_CHECK_GOTO(ret, error);
     }
 
+    lysc_update_path(ctx, NULL, "{identity}");
     COMPILE_ARRAY_UNIQUE_GOTO(ctx, submod->identities, mainmod->identities, u, lys_compile_identity, ret, error);
+    lysc_update_path(ctx, NULL, NULL);
 
     /* TODO data nodes */
 
@@ -5854,6 +6059,8 @@
     ctx.mod = mod;
     ctx.mod_def = mod;
     ctx.options = options;
+    ctx.path_len = 1;
+    ctx.path[0] = '/';
 
     mod->compiled = mod_c = calloc(1, sizeof *mod_c);
     LY_CHECK_ERR_RET(!mod_c, LOGMEM(mod->ctx), LY_EMEM);
@@ -5871,7 +6078,7 @@
     } else {
         /* features are compiled directly into the compiled module structure,
          * but it must be done in two steps to allow forward references (via if-feature) between the features themselves */
-        ret = lys_feature_precompile(ctx.ctx, ctx.mod, sp->features, &mod_c->features);
+        ret = lys_feature_precompile(&ctx, NULL, NULL, sp->features, &mod_c->features);
         LY_CHECK_GOTO(ret, error);
     }
     /* finish feature compilation, not only for the main module, but also for the submodules.
@@ -5881,17 +6088,23 @@
         ret = lys_feature_precompile_finish(&ctx, &sp->features[u], mod_c->features);
         LY_CHECK_GOTO(ret != LY_SUCCESS, error);
     }
+    lysc_update_path(&ctx, NULL, "{submodule}");
     LY_ARRAY_FOR(sp->includes, v) {
+        lysc_update_path(&ctx, NULL, sp->includes[v].name);
         LY_ARRAY_FOR(sp->includes[v].submodule->features, u) {
             ret = lys_feature_precompile_finish(&ctx, &sp->includes[v].submodule->features[u], mod_c->features);
             LY_CHECK_GOTO(ret != LY_SUCCESS, error);
         }
+        lysc_update_path(&ctx, NULL, NULL);
     }
+    lysc_update_path(&ctx, NULL, NULL);
 
+    lysc_update_path(&ctx, NULL, "{identity}");
     COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->identities, mod_c->identities, u, lys_compile_identity, ret, error);
     if (sp->identities) {
         LY_CHECK_RET(lys_compile_identities_derived(&ctx, sp->identities, mod_c->identities));
     }
+    lysc_update_path(&ctx, NULL, NULL);
 
     /* data nodes */
     LY_LIST_FOR(sp->data, node_p) {
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 9ae064b..4da52a7 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -512,6 +512,7 @@
  * the precompiled list is reused to finish the compilation to preserve pointers already used in various compiled
  * if-feature structures.
  *
+ * @param[in] ctx_sc Compile context - alternative to the combination of @p ctx and @p module.
  * @param[in] ctx libyang context.
  * @param[in] module Module of the features.
  * @param[in] features_p Array if the parsed features definitions to precompile.
@@ -520,7 +521,7 @@
  * to be processed.
  * @return LY_ERR value.
  */
-LY_ERR lys_feature_precompile(struct ly_ctx *ctx, struct lys_module *module, struct lysp_feature *features_p, struct lysc_feature **features);
+LY_ERR lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module, struct lysp_feature *features_p, struct lysc_feature **features);
 
 /**
  * @brief Get the @ref ifftokens from the given position in the 2bits array