lightningd: deprecate "message": null in channel_state_changed notifications.
What changed, and why it matters
This is a routine API cleanup, not a security fix. The commit changes how Core Lightning tells plugins about channel state changes: instead of sending `"message": null` when there is no message, it will eventually stop including the `message` field at all. For now it still sends `null` unless the user has opted into strict 'no deprecated features' mode. The code also removes a general helper for adding `null` values to JSON and adds private copies in two places that still need it temporarily.
No security action required. Plugin authors relying on `message: null` in `channel_state_changed` notifications should update their code to handle an absent `message` field before the v26.12 removal.
Security signals we found
No security-relevant signals present
Change is a documented API deprecation with no memory safety, cryptographic, or authorization changes
Evidence from the diff
The commit deprecates the message field being null in the channel_state_changed plugin notification. It removes the global json_add_null() helper from common/json_stream.c/h, replaces the header declaration with a comment discouraging null fields, and adds static local copies in lightningd/notification.c and plugins/bcli.c for remaining transitional use. In channel_state_changed_notification_serialize(), message is now omitted when absent unless deprecated behavior is allowed, in which case null is still emitted. The JSON schema and deprecation documentation are updated accordingly.
Changed components
lightningd notification subsystemchannel_state_changed plugin notificationcommon/json_stream JSON helpersplugins/bclideveloper deprecation documentation and JSON schemaInspect captured patch +21 / −11
diff --git a/common/json_stream.c b/common/json_stream.c
index 582e5e7e..b272760c 100644
--- a/common/json_stream.c
+++ b/common/json_stream.c
@@ -313,11 +313,6 @@ void json_add_bool(struct json_stream *result, const char *fieldname, bool value
json_add_primitive(result, fieldname, value ? "true" : "false");
}
-void json_add_null(struct json_stream *stream, const char *fieldname)
-{
- json_add_primitive(stream, fieldname, "null");
-}
-
void json_add_hex(struct json_stream *js, const char *fieldname,
const void *data, size_t len)
{
diff --git a/common/json_stream.h b/common/json_stream.h
index da65761c..05e2df74 100644
--- a/common/json_stream.h
+++ b/common/json_stream.h
@@ -240,8 +240,7 @@ void json_add_s32(struct json_stream *result, const char *fieldname,
void json_add_bool(struct json_stream *result, const char *fieldname,
bool value);
-/* '"fieldname" : null' or 'null' if fieldname is NULL */
-void json_add_null(struct json_stream *stream, const char *fieldname);
+/* Looking for json_add_null? Don't do that: we omit fields, don't 'null' them! */
/* '"fieldname" : "0189abcdef..."' or "0189abcdef..." if fieldname is NULL */
void json_add_hex(struct json_stream *result, const char *fieldname,
diff --git a/doc/developers-guide/deprecated-features.md b/doc/developers-guide/deprecated-features.md
index c7039221..5da91a55 100644
--- a/doc/developers-guide/deprecated-features.md
+++ b/doc/developers-guide/deprecated-features.md
@@ -25,7 +25,8 @@ privacy:
| pay_notifications.raw_fields | Field | v25.09 | v26.09 | `channel_hint_update`, `pay_failure` and `pay_success` notifications now wrap members in an object of the same name |
| encrypted_hsm | Config | v25.12 | v26.12 | `hsm-passphrase` is a name which also makes sense for modern hsm_secrets which use BIP 39 |
| newaddr.addresstype.defaultbech32 | Parameter | v25.12 | v26.12 | Use `p2tr` in the response (present since v23.08 if `addresstype` is `p2tr`, and always present since v24.12). |
-
+| channel_state_changed.null_message | Notification Field | v25.12 | v26.12 | In channel_state_changed notification, `message` will be missing instead of `null` |
+
Inevitably there are features which need to change: either to be generalized, or removed when they can no longer be supported.
Types of deprecation:
diff --git a/doc/schemas/notification/channel_state_changed.json b/doc/schemas/notification/channel_state_changed.json
index c2f1aba1..a90fddbf 100644
--- a/doc/schemas/notification/channel_state_changed.json
+++ b/doc/schemas/notification/channel_state_changed.json
@@ -15,8 +15,7 @@
"channel_id",
"timestamp",
"new_state",
- "cause",
- "message"
+ "cause"
],
"properties": {
"peer_id": {
diff --git a/lightningd/notification.c b/lightningd/notification.c
index 73a689b9..b2adb64e 100644
--- a/lightningd/notification.c
+++ b/lightningd/notification.c
@@ -273,6 +273,12 @@ void notify_channel_opened(struct lightningd *ld,
notify_send(ld, n);
}
+/* Don't use this: omit fields instead! */
+static void json_add_null(struct json_stream *stream, const char *fieldname)
+{
+ json_add_primitive(stream, fieldname, "null");
+}
+
static void channel_state_changed_notification_serialize(struct json_stream *stream,
struct lightningd *ld,
const struct node_id *peer_id,
@@ -302,8 +308,12 @@ static void channel_state_changed_notification_serialize(struct json_stream *str
json_add_string(stream, "cause", channel_change_state_reason_str(cause));
if (message != NULL)
json_add_string(stream, "message", message);
- else
+ else if (lightningd_deprecated_out_ok(ld, ld->deprecated_ok,
+ "channel_state_changed",
+ "null_message",
+ "v25.12", "v26.12")) {
json_add_null(stream, "message");
+ }
}
REGISTER_NOTIFICATION(channel_state_changed)
diff --git a/plugins/bcli.c b/plugins/bcli.c
index 51ff4942..cb99763e 100644
--- a/plugins/bcli.c
+++ b/plugins/bcli.c
@@ -418,6 +418,12 @@ static struct command_result *command_err_bcli_badjson(struct bitcoin_cli *bcli,
return command_done_err(bcli->cmd, BCLI_ERROR, err, NULL);
}
+/* Don't use this in general: it's better to omit fields. */
+static void json_add_null(struct json_stream *stream, const char *fieldname)
+{
+ json_add_primitive(stream, fieldname, "null");
+}
+
static struct command_result *process_getutxout(struct bitcoin_cli *bcli)
{
const jsmntok_t *tokens;
Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.