diff --git a/man/sd-varlink.xml b/man/sd-varlink.xml
index c7cfceb712..282d6a330e 100644
--- a/man/sd-varlink.xml
+++ b/man/sd-varlink.xml
@@ -44,6 +44,13 @@
sd-json3 API for JSON
serialization, deserialization and manipulation.
+ Canonical encoding rules: sd-varlink omits the "parameters" member on the wire in replies,
+ errors, and notifications when there are no parameters to transmit. This reduces message size and
+ avoids ambiguity. Receivers must be tolerant and accept any of the following encodings for the
+ absence of parameters: an omitted "parameters" key (preferred), a JSON null
+ value, or an empty object {}. When decoding, sd-varlink treats JSON null
+ as if the member was omitted.
+
The varlinkctl1 tool
makes the functionality implemented by sd-varlink available from the command line.
diff --git a/src/libsystemd/sd-json/json-util.h b/src/libsystemd/sd-json/json-util.h
index d0db4d8035..d5e8f57e4a 100644
--- a/src/libsystemd/sd-json/json-util.h
+++ b/src/libsystemd/sd-json/json-util.h
@@ -170,6 +170,7 @@ enum {
_JSON_BUILD_PAIR_STRV_NON_EMPTY,
_JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY,
_JSON_BUILD_PAIR_VARIANT_NON_NULL,
+ _JSON_BUILD_PAIR_VARIANT_NON_EMPTY,
/* _SD_JSON_BUILD_PAIR_VARIANT_ARRAY_NON_EMPTY, */
_JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY,
_JSON_BUILD_PAIR_IN4_ADDR_NON_NULL,
@@ -218,6 +219,7 @@ enum {
#define JSON_BUILD_PAIR_STRV_NON_EMPTY(name, l) _JSON_BUILD_PAIR_STRV_NON_EMPTY, (const char*) { name }, (char**) { l }
#define JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY(name, l) _JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY, (const char*) { name }, (char**) { l }
#define JSON_BUILD_PAIR_VARIANT_NON_NULL(name, v) _JSON_BUILD_PAIR_VARIANT_NON_NULL, (const char*) { name }, (sd_json_variant*) { v }
+#define JSON_BUILD_PAIR_VARIANT_NON_EMPTY(name, v) _JSON_BUILD_PAIR_VARIANT_NON_EMPTY, (const char*) { name }, (sd_json_variant*) { v }
#define JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY(name, v, n) _JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY, (const char*) { name }, (const void*) { v }, (size_t) { n }
#define JSON_BUILD_PAIR_IN4_ADDR_NON_NULL(name, v) _JSON_BUILD_PAIR_IN4_ADDR_NON_NULL, (const char*) { name }, (const struct in_addr*) { v }
#define JSON_BUILD_PAIR_IN6_ADDR_NON_NULL(name, v) _JSON_BUILD_PAIR_IN6_ADDR_NON_NULL, (const char*) { name }, (const struct in6_addr*) { v }
diff --git a/src/libsystemd/sd-json/sd-json.c b/src/libsystemd/sd-json/sd-json.c
index 77f7524d2a..79cfa4cc60 100644
--- a/src/libsystemd/sd-json/sd-json.c
+++ b/src/libsystemd/sd-json/sd-json.c
@@ -4445,7 +4445,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4475,7 +4475,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4503,7 +4503,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4530,7 +4530,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4567,7 +4567,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4593,7 +4593,33 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
add_more = sd_json_variant_ref(v);
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
+
+ current->expect = EXPECT_OBJECT_KEY;
+ break;
+ }
+
+ case _JSON_BUILD_PAIR_VARIANT_NON_EMPTY: {
+ sd_json_variant *v;
+ const char *n;
+
+ if (current->expect != EXPECT_OBJECT_KEY) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ n = va_arg(ap, const char *);
+ v = va_arg(ap, sd_json_variant *);
+
+ if (v && !sd_json_variant_is_blank_object(v) && current->n_suppress == 0) {
+ r = sd_json_variant_new_string(&add, n);
+ if (r < 0)
+ goto finish;
+
+ add_more = sd_json_variant_ref(v);
+ }
+
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4623,7 +4649,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4651,7 +4677,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4679,7 +4705,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4709,7 +4735,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4737,7 +4763,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4765,7 +4791,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4795,7 +4821,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4825,7 +4851,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4887,7 +4913,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
}
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4923,7 +4949,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
@@ -4951,7 +4977,7 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
diff --git a/src/libsystemd/sd-varlink/sd-varlink-idl.c b/src/libsystemd/sd-varlink/sd-varlink-idl.c
index 7e6680e0e6..03ed52f172 100644
--- a/src/libsystemd/sd-varlink/sd-varlink-idl.c
+++ b/src/libsystemd/sd-varlink/sd-varlink-idl.c
@@ -5,6 +5,7 @@
#include "alloc-util.h"
#include "ansi-color.h"
#include "extract-word.h"
+#include "json-internal.h"
#include "json-util.h"
#include "log.h"
#include "memstream-util.h"
@@ -1793,11 +1794,9 @@ static int varlink_idl_validate_symbol(const sd_varlink_symbol *symbol, sd_json_
assert(symbol);
assert(!IN_SET(symbol->symbol_type, _SD_VARLINK_SYMBOL_COMMENT, _SD_VARLINK_INTERFACE_COMMENT));
- if (!v) {
- if (reterr_bad_field)
- *reterr_bad_field = NULL;
- return varlink_idl_log(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Null object passed, refusing.");
- }
+ /* Consider a NULL pointer equivalent to an empty object */
+ if (!v)
+ v = JSON_VARIANT_MAGIC_EMPTY_OBJECT;
switch (symbol->symbol_type) {
diff --git a/src/libsystemd/sd-varlink/sd-varlink.c b/src/libsystemd/sd-varlink/sd-varlink.c
index e0d5bf20aa..6e50073049 100644
--- a/src/libsystemd/sd-varlink/sd-varlink.c
+++ b/src/libsystemd/sd-varlink/sd-varlink.c
@@ -1020,6 +1020,7 @@ static int varlink_test_timeout(sd_varlink *v) {
}
static int varlink_dispatch_local_error(sd_varlink *v, const char *error) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *empty = NULL;
int r;
assert(v);
@@ -1028,7 +1029,11 @@ static int varlink_dispatch_local_error(sd_varlink *v, const char *error) {
if (!v->reply_callback)
return 0;
- r = v->reply_callback(v, NULL, error, SD_VARLINK_REPLY_ERROR|SD_VARLINK_REPLY_LOCAL, v->userdata);
+ r = sd_json_variant_new_object(&empty, NULL, 0);
+ if (r < 0)
+ return r;
+
+ r = v->reply_callback(v, empty, error, SD_VARLINK_REPLY_ERROR|SD_VARLINK_REPLY_LOCAL, v->userdata);
if (r < 0)
varlink_log_errno(v, r, "Reply callback returned error, ignoring: %m");
@@ -1061,25 +1066,23 @@ static int varlink_dispatch_disconnect(sd_varlink *v) {
return 1;
}
-static int varlink_sanitize_parameters(sd_json_variant **v) {
+static int varlink_sanitize_incoming_parameters(sd_json_variant **v) {
int r;
-
assert(v);
- /* Varlink always wants a parameters list, hence make one if the caller doesn't want any */
- if (!*v)
- return sd_json_variant_new_object(v, NULL, 0);
- if (sd_json_variant_is_null(*v)) {
- sd_json_variant *empty;
-
+ /* Convert NULL or JSON null to empty object for method handlers (backward compatibility) */
+ if (!*v || sd_json_variant_is_null(*v)) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *empty = NULL;
r = sd_json_variant_new_object(&empty, NULL, 0);
if (r < 0)
return r;
-
+ /* sd_json_variant_unref() is a NOP if *v is NULL */
sd_json_variant_unref(*v);
- *v = empty;
+ *v = TAKE_PTR(empty);
return 0;
}
+
+ /* Ensure we have an object */
if (!sd_json_variant_is_object(*v))
return -EINVAL;
@@ -1146,7 +1149,7 @@ static int varlink_dispatch_reply(sd_varlink *v) {
if (error && FLAGS_SET(flags, SD_VARLINK_REPLY_CONTINUES))
goto invalid;
- r = varlink_sanitize_parameters(¶meters);
+ r = varlink_sanitize_incoming_parameters(¶meters);
if (r < 0)
goto invalid;
@@ -1327,7 +1330,7 @@ static int varlink_dispatch_method(sd_varlink *v) {
if (!method)
goto invalid;
- r = varlink_sanitize_parameters(¶meters);
+ r = varlink_sanitize_incoming_parameters(¶meters);
if (r < 0)
goto fail;
@@ -1575,13 +1578,14 @@ _public_ int sd_varlink_get_current_parameters(sd_varlink *v, sd_json_variant **
if (!v->current)
return -ENODATA;
+ if (!ret)
+ return 0;
+
p = sd_json_variant_by_key(v->current, "parameters");
- if (!p)
- return -ENODATA;
-
- if (ret)
- *ret = sd_json_variant_ref(p);
+ if (!p || sd_json_variant_is_null(p))
+ return sd_json_variant_new_object(ret, NULL, 0);
+ *ret = sd_json_variant_ref(p);
return 0;
}
@@ -2024,14 +2028,10 @@ _public_ int sd_varlink_send(sd_varlink *v, const char *method, sd_json_variant
if (!IN_SET(v->state, VARLINK_IDLE_CLIENT, VARLINK_AWAITING_REPLY))
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection busy.");
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
r = sd_json_buildo(
&m,
SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)),
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters),
SD_JSON_BUILD_PAIR("oneway", SD_JSON_BUILD_BOOLEAN(true)));
if (r < 0)
return varlink_log_errno(v, r, "Failed to build json message: %m");
@@ -2076,14 +2076,10 @@ _public_ int sd_varlink_invoke(sd_varlink *v, const char *method, sd_json_varian
if (!IN_SET(v->state, VARLINK_IDLE_CLIENT, VARLINK_AWAITING_REPLY))
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection busy.");
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
r = sd_json_buildo(
&m,
SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)));
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters));
if (r < 0)
return varlink_log_errno(v, r, "Failed to build json message: %m");
@@ -2130,14 +2126,10 @@ _public_ int sd_varlink_observe(sd_varlink *v, const char *method, sd_json_varia
if (v->state != VARLINK_IDLE_CLIENT)
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection busy.");
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
r = sd_json_buildo(
&m,
SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)),
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters),
SD_JSON_BUILD_PAIR("more", SD_JSON_BUILD_BOOLEAN(true)));
if (r < 0)
return varlink_log_errno(v, r, "Failed to build json message: %m");
@@ -2195,14 +2187,10 @@ _public_ int sd_varlink_call_full(
* that we can assign a new reply shortly. */
varlink_clear_current(v);
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
r = sd_json_buildo(
&m,
SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)));
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters));
if (r < 0)
return varlink_log_errno(v, r, "Failed to build json message: %m");
@@ -2353,14 +2341,10 @@ _public_ int sd_varlink_collect_full(
* that we can assign a new reply shortly. */
varlink_clear_current(v);
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
r = sd_json_buildo(
&m,
SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)),
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters),
SD_JSON_BUILD_PAIR("more", SD_JSON_BUILD_BOOLEAN(true)));
if (r < 0)
return varlink_log_errno(v, r, "Failed to build json message: %m");
@@ -2501,14 +2485,7 @@ _public_ int sd_varlink_reply(sd_varlink *v, sd_json_variant *parameters) {
VARLINK_PENDING_METHOD, VARLINK_PENDING_METHOD_MORE))
return -EBUSY;
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
- r = sd_json_buildo(&m, SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)));
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to build json message: %m");
-
+ /* Validate parameters BEFORE sanitization */
if (v->current_method) {
const char *bad_field = NULL;
@@ -2519,6 +2496,10 @@ _public_ int sd_varlink_reply(sd_varlink *v, sd_json_variant *parameters) {
v->current_method->name, strna(bad_field));
}
+ r = sd_json_buildo(&m, JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters));
+ if (r < 0)
+ return varlink_log_errno(v, r, "Failed to build json message: %m");
+
r = varlink_enqueue_json(v, m);
if (r < 0)
return varlink_log_errno(v, r, "Failed to enqueue json message: %m");
@@ -2588,17 +2569,7 @@ _public_ int sd_varlink_error(sd_varlink *v, const char *error_id, sd_json_varia
* the callers don't need to do this explicitly. */
sd_varlink_reset_fds(v);
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
- r = sd_json_buildo(
- &m,
- SD_JSON_BUILD_PAIR("error", SD_JSON_BUILD_STRING(error_id)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)));
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to build json message: %m");
-
+ /* Validate parameters BEFORE sanitization */
sd_varlink_symbol *symbol = hashmap_get(v->server->symbols, error_id);
if (!symbol)
varlink_log(v, "No interface description defined for error '%s', not validating.", error_id);
@@ -2612,6 +2583,13 @@ _public_ int sd_varlink_error(sd_varlink *v, const char *error_id, sd_json_varia
error_id, strna(bad_field));
}
+ r = sd_json_buildo(
+ &m,
+ SD_JSON_BUILD_PAIR("error", SD_JSON_BUILD_STRING(error_id)),
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters));
+ if (r < 0)
+ return varlink_log_errno(v, r, "Failed to build json message: %m");
+
r = varlink_enqueue_json(v, m);
if (r < 0)
return varlink_log_errno(v, r, "Failed to enqueue json message: %m");
@@ -2726,17 +2704,7 @@ _public_ int sd_varlink_notify(sd_varlink *v, sd_json_variant *parameters) {
if (!IN_SET(v->state, VARLINK_PROCESSING_METHOD_MORE, VARLINK_PENDING_METHOD_MORE))
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection busy.");
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
- r = sd_json_buildo(
- &m,
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)),
- SD_JSON_BUILD_PAIR("continues", SD_JSON_BUILD_BOOLEAN(true)));
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to build json message: %m");
-
+ /* Validate parameters BEFORE sanitization */
if (v->current_method) {
const char *bad_field = NULL;
@@ -2750,6 +2718,13 @@ _public_ int sd_varlink_notify(sd_varlink *v, sd_json_variant *parameters) {
v->current_method->name, strna(bad_field));
}
+ r = sd_json_buildo(
+ &m,
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters),
+ SD_JSON_BUILD_PAIR("continues", SD_JSON_BUILD_BOOLEAN(true)));
+ if (r < 0)
+ return varlink_log_errno(v, r, "Failed to build json message: %m");
+
r = varlink_enqueue_json(v, m);
if (r < 0)
return varlink_log_errno(v, r, "Failed to enqueue json message: %m");
@@ -2783,6 +2758,7 @@ _public_ int sd_varlink_dispatch(sd_varlink *v, sd_json_variant *parameters, con
/* A wrapper around json_dispatch_full() that returns a nice InvalidParameter error if we hit a problem with some field. */
+ /* sd_json_dispatch_full() now handles NULL parameters gracefully */
r = sd_json_dispatch_full(parameters, dispatch_table, /* bad= */ NULL, /* flags= */ 0, userdata, &bad_field);
if (r < 0) {
if (bad_field)
diff --git a/src/shared/varlink-io.systemd.service.c b/src/shared/varlink-io.systemd.service.c
index d130f0df51..8cbf299e07 100644
--- a/src/shared/varlink-io.systemd.service.c
+++ b/src/shared/varlink-io.systemd.service.c
@@ -47,6 +47,7 @@ int varlink_method_ping(sd_varlink *link, sd_json_variant *parameters, sd_varlin
int r;
assert(link);
+ assert(parameters);
r = sd_varlink_dispatch(link, parameters, /* dispatch_table= */ NULL, /* userdata= */ NULL);
if (r != 0)
diff --git a/src/test/test-varlink.c b/src/test/test-varlink.c
index b298b9cfbf..0b834fad17 100644
--- a/src/test/test-varlink.c
+++ b/src/test/test-varlink.c
@@ -205,6 +205,9 @@ static int overload_reply(sd_varlink *link, sd_json_variant *parameters, const c
log_debug("Over reply triggered with error: %s", strna(error_id));
ASSERT_STREQ(error_id, SD_VARLINK_ERROR_DISCONNECTED);
+ /* Local disconnect errors carry empty parameters. Ensure we propagate
+ * a consistent empty object for API reliability. */
+ ASSERT_TRUE(sd_json_variant_is_blank_object(parameters));
sd_event_exit(sd_varlink_get_event(link), 0);
return 0;