Skip to content

Leafref to a top-level default is refused, depending on which modules are validated first #2605

Open

Description

@lamestllama

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

Activity

  1. added a commit that references this issue on Nov 3, 2026
    5977ad5
  2. michalvasko commented on Nov 3, 2026

    @michalvasko
    Member

    Yes, the order of the resolution was bugged but only a simple fix was needed, should work in latest devel.

  3. added
    is:bugBug description.
    status:completedFrom the developer perspective, the issue was solved (bug fixed, question answered,...)
    on Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    is:bugBug description.status:completedFrom the developer perspective, the issue was solved (bug fixed, question answered,...)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions