Repository navigation
Description
libyang version: devel at fab861b (project version 6.4.19). The same output is produced by 6.4.13 (f7e0cc1).
Reproducer
b.yang:
module b {
yang-version 1.1;
namespace "urn:example:b";
prefix b;
leaf t {
type uint8;
default 7;
}
leaf ref {
type leafref {
path "/b:t";
}
}
}ref.xml:
<ref xmlns="urn:example:b">7</ref>$ yanglint b.yang ref.xml
libyang err : Invalid leafref value "7" - no target instance "/b:t" with the same value. (/b:ref)
YANGLINT[E]: Failed to parse input data file "ref.xml".
With -e (LYD_VALIDATE_PRESENT) the same data are accepted:
$ yanglint -e b.yang ref.xml
$ echo $?
0
-e does not help once data of a module whose name sorts before b are present. a.yang:
module a {
yang-version 1.1;
namespace "urn:example:a";
prefix a;
import b {
prefix b;
}
leaf x {
type leafref {
path "/b:t";
}
}
}ax.xml:
<x xmlns="urn:example:a">7</x>
<ref xmlns="urn:example:b">7</ref>$ yanglint -e b.yang a.yang ax.xml
libyang err : Invalid leafref value "7" - no target instance "/b:t" with the same value. (/b:ref)
libyang err : Invalid leafref value "7" - no target instance "/b:t" with the same value. (/a:x)
YANGLINT[E]: Failed to parse input data file "ax.xml".
Renaming module a to c (and nothing else) makes the same data valid.
Expected behaviour
Both documents are valid. t is a top-level leaf with a default, so its default is in use, and a default in use satisfies a leafref.
RFC 7950, section 7.6.1:
If no such ancestor exists in the schema tree, the default value MUST be used.
Section 6.4.1:
In the accessible tree, all leafs and leaf-lists with default values in use exist (see Sections 7.6.1 and 7.7.2).
Section 9.9:
If the "require-instance" property (Section 9.9.3) is "true", there MUST exist a node in the data tree, or a node with a default value in use (see Sections 7.6.1 and 7.7.2), of the referred schema tree leaf or leaf-list node with the same value as the leafref value in a valid data tree.
Module names and the set of loaded modules should not change the verdict.
Where it seems to come from
lyd_validate() in validation.c handles one module per loop iteration: it creates that module's top-level defaults (lyd_new_implicit_r()), then calls lyd_validate_unres(). The node_when and node_types sets passed to lyd_validate_unres() were filled by the parser for the whole tree, so every leafref, union and when in the document is resolved during the first iteration, before the top-level defaults of the later modules exist. Without LYD_VALIDATE_PRESENT the first module is an internal one (lyd_mod_next_module()); with it, the first module is the one whose top-level data sort first, and top-level data are ordered by module name (lyd_insert_get_next_anchor()).
when conditions are affected in the same way: a sibling leaf w { when "/b:t = 7"; type string; } written as <w>x</w> is refused by yanglint and accepted by yanglint -e.
The same ordering makes XML and JSON disagree on a union member
u.yang:
module u {
yang-version 1.1;
namespace "urn:example:u";
prefix u;
leaf t {
type uint8;
default 7;
}
leaf v {
type union {
type leafref {
path "/u:t";
}
type string;
}
}
}v.xml is <v xmlns="urn:example:u">7</v> and v.json is {"u:v": 7}.
$ yanglint -f json u.yang v.xml
{
"u:v": "7"
}
$ yanglint -f json u.yang v.json
libyang err : Invalid union value "7" - no matching subtype found:
ly2 leafref: Invalid leafref value "7" - no target instance "/u:t" with the same value.
ly2 string: Invalid non-string-encoded string value "7".
(/u:v)
YANGLINT[E]: Failed to parse input data file "v.json".
$ yanglint -e -f json u.yang v.xml
{
"u:v": 7
}
$ yanglint -e -f json u.yang v.json
{
"u:v": 7
}
The XML value is stored with the string member because the leafref member was tried before t existed; the stored member is kept although the final tree satisfies the leafref member. RFC 7950, section 9.12: "a value is validated consecutively against each member type, in the order they are specified in the "type" statement, until a match is found."
The parser and lyd_validate_all() after LYD_PARSE_ONLY disagree
A nested default is created while its parent is parsed, so the parser accepts the following data, but lyd_validate_all() on the same tree parsed with LYD_PARSE_ONLY (what yanglint -m does) creates it only in the iteration of its own module.
z.yang:
module z {
yang-version 1.1;
namespace "urn:example:z";
prefix z;
container c {
leaf n {
type uint8;
default 7;
}
leaf w {
type string;
}
}
}a.yang:
module a {
yang-version 1.1;
namespace "urn:example:a";
prefix a;
import z {
prefix z;
}
leaf x {
type leafref {
path "/z:c/z:n";
}
}
}data-az.xml:
<x xmlns="urn:example:a">7</x>
<c xmlns="urn:example:z">
<w>w</w>
</c>$ yanglint -e z.yang a.yang data-az.xml
$ echo $?
0
$ yanglint -e -m z.yang a.yang data-az.xml
libyang err : Invalid leafref value "7" - no target instance "/z:c/z:n" with the same value. (/a:x)
YANGLINT[E]: Merged data are not valid.
Renaming a to za, which sorts after z, makes -m accept too.
Why it matters
A leafref to a defaulted leaf is ordinary YANG, and whether a valid document is accepted currently depends on module names and on which other modules are loaded.
Related
- Implicit defaults without validation #2437 (implicit defaults and validation of the rest of the tree), which may share the cause.
Activity
validation BUGFIX unres when/types at the end
michalvasko commentedon Nov 3, 2026
Yes, the order of the resolution was bugged but only a simple fix was needed, should work in latest
devel.