From d90d0d2a9c85822a2433a15a91bbc1fb47e47eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= Date: Fri, 12 Jun 2026 15:10:20 +0200 Subject: [PATCH] yangerd: Return {} instead of 404 if no match --- src/yangerd/internal/ipc/server.go | 11 ++++++++--- src/yangerd/internal/ipc/server_test.go | 9 +++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/yangerd/internal/ipc/server.go b/src/yangerd/internal/ipc/server.go index a44fd1db..b4c9b646 100644 --- a/src/yangerd/internal/ipc/server.go +++ b/src/yangerd/internal/ipc/server.go @@ -131,10 +131,15 @@ func (s *Server) handleGet(conn net.Conn, req *Request) { data := s.tree.Get(key) if data == nil { + // An absent subtree is a normal answer for operational data -- + // the feature is simply not active (e.g. NTP unconfigured). + // Answer ok with an empty object rather than an error, so every + // client gets "no data" without special-casing. Deliberately + // NOT {"": {}}: that would make libyang instantiate the + // container, which for presence containers is real data. WriteResponse(conn, &Response{ - Status: "error", - Code: 404, - Message: "path not found: " + path, + Status: "ok", + Data: json.RawMessage(`{}`), }) return } diff --git a/src/yangerd/internal/ipc/server_test.go b/src/yangerd/internal/ipc/server_test.go index 73b421bf..f329308b 100644 --- a/src/yangerd/internal/ipc/server_test.go +++ b/src/yangerd/internal/ipc/server_test.go @@ -33,8 +33,13 @@ func TestServerGetNotFound(t *testing.T) { tr := tree.New() resp := serverRoundTrip(t, tr, true, &Request{Method: "get", Path: "/nonexistent"}) - if resp.Status != "error" || resp.Code != 404 { - t.Fatalf("expected 404 error, got %+v", resp) + // An absent subtree is "no data", not an error: ok + empty object, + // so clients (statd, yangerctl) need no special-casing. + if resp.Status != "ok" { + t.Fatalf("expected ok, got %+v", resp) + } + if string(resp.Data) != "{}" { + t.Fatalf("expected empty object data, got %s", resp.Data) } }