From 7504822c1d107946af1ca2203cc73eb48cb116b0 Mon Sep 17 00:00:00 2001 From: swethasukumarr Date: Fri, 17 Jul 2026 14:13:17 -0400 Subject: [PATCH 1/5] Replace SubscriptionData value map with shared_ptr; drop stale notifications via weak_ptr --- src/gateway.cpp | 18 +++++- src/helpers_impl.h | 45 ++++++++++---- test/unit/helperTest.cpp | 125 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 12 deletions(-) diff --git a/src/gateway.cpp b/src/gateway.cpp index 9a1e028..7b22bab 100644 --- a/src/gateway.cpp +++ b/src/gateway.cpp @@ -361,7 +361,23 @@ class Server for (auto& callback : notification.callbacks) { - callback.lambda(callback.usercb, notification.params); + try + { + callback.lambda(callback.usercb, notification.params); + } + catch (const std::bad_any_cast& e) + { + FIREBOLT_LOG_ERROR("Gateway", + "[notification-worker] bad_any_cast dispatching event='%s': %s — " + "subscription may have been removed while notification was in-flight", + callback.eventName.c_str(), e.what()); + } + catch (const std::exception& e) + { + FIREBOLT_LOG_ERROR("Gateway", + "[notification-worker] exception dispatching event='%s': %s", + callback.eventName.c_str(), e.what()); + } } } } diff --git a/src/helpers_impl.h b/src/helpers_impl.h index 82b1e5b..3ed2c5c 100644 --- a/src/helpers_impl.h +++ b/src/helpers_impl.h @@ -18,6 +18,7 @@ #include "firebolt/gateway.h" #include "firebolt/helpers.h" +#include namespace Firebolt::Helpers { @@ -35,8 +36,8 @@ class HelperImpl : public IHelper std::lock_guard lock(mutex_); for (auto& subscription : subscriptions_) { - void* notificationPtr = reinterpret_cast(&subscription.second); - gateway_.unsubscribe(subscription.second.eventName, notificationPtr); + void* notificationPtr = reinterpret_cast(subscription.second.get()); + gateway_.unsubscribe(subscription.second->eventName, notificationPtr); } subscriptions_.clear(); } @@ -76,8 +77,8 @@ class HelperImpl : public IHelper { return Result{Error::General}; } - void* notificationPtr = reinterpret_cast(&it->second); - auto errorStatus{gateway_.unsubscribe(it->second.eventName, notificationPtr)}; + void* notificationPtr = reinterpret_cast(it->second.get()); + auto errorStatus{gateway_.unsubscribe(it->second->eventName, notificationPtr)}; subscriptions_.erase(it); return Result{errorStatus}; } @@ -87,10 +88,10 @@ class HelperImpl : public IHelper std::lock_guard lock(mutex_); for (auto it = subscriptions_.begin(); it != subscriptions_.end();) { - if (it->second.owner == owner) + if (it->second->owner == owner) { - void* notificationPtr = reinterpret_cast(&it->second); - gateway_.unsubscribe(it->second.eventName, notificationPtr); + void* notificationPtr = reinterpret_cast(it->second.get()); + gateway_.unsubscribe(it->second->eventName, notificationPtr); it = subscriptions_.erase(it); } else @@ -116,10 +117,32 @@ class HelperImpl : public IHelper { std::lock_guard lock(mutex_); uint64_t newId = currentId_++; - subscriptions_[newId] = SubscriptionData{owner, eventName, std::move(notification)}; - void* notificationPtr = reinterpret_cast(&subscriptions_[newId]); + auto spData = std::make_shared(SubscriptionData{owner, eventName, std::move(notification)}); + subscriptions_[newId] = spData; + void* notificationPtr = reinterpret_cast(spData.get()); - Error status = gateway_.subscribe(eventName, callback, notificationPtr); + // Guard the callback with a weak_ptr so that any notification already queued + // to the async worker thread at the time of unsubscribe is safely dropped + // rather than invoking the callback through a dangling pointer. This closes + // the race between Server::notify() copying callbacks under eventMap_mtx and + // the worker dispatching them after SubscriptionData has been destroyed. + std::weak_ptr wpData = spData; + Firebolt::Transport::EventCallback wrappedCallback = + [wpData, callback, eventName](void* usercb, const nlohmann::json& json) + { + if (auto sp = wpData.lock()) + { + callback(usercb, json); + } + else + { + FIREBOLT_LOG_DEBUG("Helper", + "[subscription] notification dropped for already-unsubscribed event='%s'", + eventName.c_str()); + } + }; + + Error status = gateway_.subscribe(eventName, wrappedCallback, notificationPtr); if (Error::None != status) { @@ -131,7 +154,7 @@ class HelperImpl : public IHelper Firebolt::Transport::IGateway& gateway_; std::mutex mutex_; - std::map subscriptions_; + std::map> subscriptions_; uint64_t currentId_{0}; }; } // namespace Firebolt::Helpers diff --git a/test/unit/helperTest.cpp b/test/unit/helperTest.cpp index be93df3..be40820 100644 --- a/test/unit/helperTest.cpp +++ b/test/unit/helperTest.cpp @@ -605,3 +605,128 @@ TEST(GetHelperInstanceTest, ReturnsSingleton) // Same singleton reference EXPECT_EQ(&helper1, &helper2); } + +// --------------------------------------------------------------------------- +// Race condition fix tests (MONUI-908) +// +// These tests validate the weak_ptr guard introduced in HelperImpl::subscribe. +// The guard prevents a use-after-free crash that occurred when: +// 1. The platform sent an event notification (e.g. onSpeechInterrupted). +// 2. Before the worker thread dispatched it, unsubscribeAll() ran and +// destroyed the SubscriptionData object. +// 3. The worker thread then invoked the registered callback with a dangling +// void* pointer → bad_any_cast → std::terminate → device crash. +TEST_F(HelperUTest, WeakPtrGuard_NotificationDeliveredWhileSubscribed) +{ + Firebolt::Transport::EventCallback capturedCallback; + void* capturedUsercb = nullptr; + + EXPECT_CALL(mockGateway, subscribe("test.onEvent", _, _)) + .WillOnce([&](const std::string&, Firebolt::Transport::EventCallback cb, void* ucb) { + capturedCallback = cb; + capturedUsercb = ucb; + return Error::None; + }); + + std::promise deliveredValue; + auto future = deliveredValue.get_future(); + std::function notification = [&deliveredValue](int val) { deliveredValue.set_value(val); }; + + IHelper& ihelper = helper; + auto subResult = + ihelper.subscribe(this, "test.onEvent", std::move(notification), onPropertyChangedCallback); + ASSERT_TRUE(subResult); + ASSERT_TRUE(capturedCallback) << "wrappedCallback must have been captured"; + + // Worker thread dispatches notification while subscription is still alive + capturedCallback(capturedUsercb, nlohmann::json{{"value", 42}}); + + auto status = future.wait_for(std::chrono::seconds(1)); + ASSERT_EQ(status, std::future_status::ready); + EXPECT_EQ(future.get(), 42); + + // Destructor will unsubscribe the remaining active subscription + EXPECT_CALL(mockGateway, unsubscribe("test.onEvent", _)).WillOnce(Return(Error::None)); +} + +// --------------------------------------------------------------------------- +// Test name: HelperUTest.WeakPtrGuard_NotificationDroppedAfterUnsubscribeAll +// Covers: wrappedCallback drops in-flight notification when SubscriptionData +// has been destroyed by unsubscribeAll() — the exact MONUI-908 race. +// +// Before fix: capturedUsercb is dangling → bad_any_cast → std::terminate +// After fix: wpData.lock() returns nullptr → silently dropped, no crash +// +// Scenario type: race condition regression test +// --------------------------------------------------------------------------- +TEST_F(HelperUTest, WeakPtrGuard_NotificationDroppedAfterUnsubscribeAll) +{ + Firebolt::Transport::EventCallback capturedCallback; + void* capturedUsercb = nullptr; + + EXPECT_CALL(mockGateway, subscribe("tts.onSpeechInterrupted", _, _)) + .WillOnce([&](const std::string&, Firebolt::Transport::EventCallback cb, void* ucb) { + capturedCallback = cb; + capturedUsercb = ucb; + return Error::None; + }); + + bool notificationFired = false; + std::function notification = [¬ificationFired](int) { notificationFired = true; }; + + IHelper& ihelper = helper; + ihelper.subscribe(this, "tts.onSpeechInterrupted", std::move(notification), + onPropertyChangedCallback); + + // Lifecycle background event fires → Monarch calls unsubscribeAll() + // shared_ptr ref count drops to 0 → SubscriptionData is destroyed + EXPECT_CALL(mockGateway, unsubscribe("tts.onSpeechInterrupted", _)).WillOnce(Return(Error::None)); + helper.unsubscribeAll(this); + + // Platform sends onSpeechInterrupted — notification was already queued in-flight + // Replay the race: call the wrappedCallback with the now-stale usercb address. + ASSERT_TRUE(capturedCallback) << "wrappedCallback must have been captured"; + capturedCallback(capturedUsercb, nlohmann::json{{"value", 1}}); + + // Notification must NOT be delivered to Monarch + EXPECT_FALSE(notificationFired); + // Reaching this line without crashing IS the primary assertion of this test +} + +// --------------------------------------------------------------------------- +// Test name: HelperUTest.WeakPtrGuard_NotificationDroppedAfterUnsubscribeById +// Covers: Same weak_ptr guard works for single unsubscribe (not just unsubscribeAll) +// Scenario type: race condition regression test +// --------------------------------------------------------------------------- +TEST_F(HelperUTest, WeakPtrGuard_NotificationDroppedAfterUnsubscribeById) +{ + Firebolt::Transport::EventCallback capturedCallback; + void* capturedUsercb = nullptr; + + EXPECT_CALL(mockGateway, subscribe("test.onEvent", _, _)) + .WillOnce([&](const std::string&, Firebolt::Transport::EventCallback cb, void* ucb) { + capturedCallback = cb; + capturedUsercb = ucb; + return Error::None; + }); + + bool notificationFired = false; + std::function notification = [¬ificationFired](int) { notificationFired = true; }; + + IHelper& ihelper = helper; + auto subResult = + ihelper.subscribe(this, "test.onEvent", std::move(notification), onPropertyChangedCallback); + ASSERT_TRUE(subResult); + SubscriptionId id = *subResult; + + // Unsubscribe by specific ID → SubscriptionData destroyed + EXPECT_CALL(mockGateway, unsubscribe("test.onEvent", _)).WillOnce(Return(Error::None)); + helper.unsubscribe(id); + + // In-flight notification arrives after the subscription was removed + ASSERT_TRUE(capturedCallback) << "wrappedCallback must have been captured"; + capturedCallback(capturedUsercb, nlohmann::json{{"value", 99}}); + + EXPECT_FALSE(notificationFired); + // Reaching this line without crashing IS the primary assertion of this test +} From 0ccc90533f43ffd9d8f33b32a1a7ed1bb4279dbb Mon Sep 17 00:00:00 2001 From: swethasukumarr Date: Fri, 17 Jul 2026 14:29:06 -0400 Subject: [PATCH 2/5] Address copilot comments --- src/gateway.cpp | 1 + test/unit/helperTest.cpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gateway.cpp b/src/gateway.cpp index 7b22bab..09b5390 100644 --- a/src/gateway.cpp +++ b/src/gateway.cpp @@ -22,6 +22,7 @@ #include "firebolt/types.h" #include "transport.h" #include "utils.h" +#include #include #include #include diff --git a/test/unit/helperTest.cpp b/test/unit/helperTest.cpp index be40820..b89cd65 100644 --- a/test/unit/helperTest.cpp +++ b/test/unit/helperTest.cpp @@ -675,8 +675,9 @@ TEST_F(HelperUTest, WeakPtrGuard_NotificationDroppedAfterUnsubscribeAll) std::function notification = [¬ificationFired](int) { notificationFired = true; }; IHelper& ihelper = helper; - ihelper.subscribe(this, "tts.onSpeechInterrupted", std::move(notification), - onPropertyChangedCallback); + auto subResult = ihelper.subscribe(this, "tts.onSpeechInterrupted", std::move(notification), + onPropertyChangedCallback); + ASSERT_TRUE(subResult); // Lifecycle background event fires → Monarch calls unsubscribeAll() // shared_ptr ref count drops to 0 → SubscriptionData is destroyed From 2e330daa026ea852ff1ae9648b336679d1299a85 Mon Sep 17 00:00:00 2001 From: swethasukumarr Date: Fri, 17 Jul 2026 14:41:06 -0400 Subject: [PATCH 3/5] Address copilot comments --- src/gateway.cpp | 10 +++++--- src/helpers_impl.h | 19 ++++++++------- test/unit/gatewayTest.cpp | 50 +++++++++++++++++++++++++++++++++++++++ test/unit/helperTest.cpp | 36 ++++++++++++++++------------ 4 files changed, 88 insertions(+), 27 deletions(-) diff --git a/src/gateway.cpp b/src/gateway.cpp index 09b5390..c963338 100644 --- a/src/gateway.cpp +++ b/src/gateway.cpp @@ -370,15 +370,19 @@ class Server { FIREBOLT_LOG_ERROR("Gateway", "[notification-worker] bad_any_cast dispatching event='%s': %s — " - "subscription may have been removed while notification was in-flight", + "notification type does not match the registered callback signature", callback.eventName.c_str(), e.what()); } catch (const std::exception& e) { - FIREBOLT_LOG_ERROR("Gateway", - "[notification-worker] exception dispatching event='%s': %s", + FIREBOLT_LOG_ERROR("Gateway", "[notification-worker] exception dispatching event='%s': %s", callback.eventName.c_str(), e.what()); } + catch (...) + { + FIREBOLT_LOG_ERROR("Gateway", "[notification-worker] unknown exception dispatching event='%s'", + callback.eventName.c_str()); + } } } } diff --git a/src/helpers_impl.h b/src/helpers_impl.h index 3ed2c5c..bb7fabf 100644 --- a/src/helpers_impl.h +++ b/src/helpers_impl.h @@ -18,7 +18,9 @@ #include "firebolt/gateway.h" #include "firebolt/helpers.h" +#include #include +#include namespace Firebolt::Helpers { @@ -36,7 +38,7 @@ class HelperImpl : public IHelper std::lock_guard lock(mutex_); for (auto& subscription : subscriptions_) { - void* notificationPtr = reinterpret_cast(subscription.second.get()); + void* notificationPtr = static_cast(subscription.second.get()); gateway_.unsubscribe(subscription.second->eventName, notificationPtr); } subscriptions_.clear(); @@ -77,7 +79,7 @@ class HelperImpl : public IHelper { return Result{Error::General}; } - void* notificationPtr = reinterpret_cast(it->second.get()); + void* notificationPtr = static_cast(it->second.get()); auto errorStatus{gateway_.unsubscribe(it->second->eventName, notificationPtr)}; subscriptions_.erase(it); return Result{errorStatus}; @@ -90,7 +92,7 @@ class HelperImpl : public IHelper { if (it->second->owner == owner) { - void* notificationPtr = reinterpret_cast(it->second.get()); + void* notificationPtr = static_cast(it->second.get()); gateway_.unsubscribe(it->second->eventName, notificationPtr); it = subscriptions_.erase(it); } @@ -119,7 +121,7 @@ class HelperImpl : public IHelper uint64_t newId = currentId_++; auto spData = std::make_shared(SubscriptionData{owner, eventName, std::move(notification)}); subscriptions_[newId] = spData; - void* notificationPtr = reinterpret_cast(spData.get()); + void* notificationPtr = static_cast(spData.get()); // Guard the callback with a weak_ptr so that any notification already queued // to the async worker thread at the time of unsubscribe is safely dropped @@ -128,21 +130,20 @@ class HelperImpl : public IHelper // the worker dispatching them after SubscriptionData has been destroyed. std::weak_ptr wpData = spData; Firebolt::Transport::EventCallback wrappedCallback = - [wpData, callback, eventName](void* usercb, const nlohmann::json& json) + [wpData, callback, eventName](void* /*usercb*/, const nlohmann::json& json) { if (auto sp = wpData.lock()) { - callback(usercb, json); + callback(sp.get(), json); } else { - FIREBOLT_LOG_DEBUG("Helper", - "[subscription] notification dropped for already-unsubscribed event='%s'", + FIREBOLT_LOG_DEBUG("Helper", "[subscription] notification dropped for already-unsubscribed event='%s'", eventName.c_str()); } }; - Error status = gateway_.subscribe(eventName, wrappedCallback, notificationPtr); + Error status = gateway_.subscribe(eventName, std::move(wrappedCallback), notificationPtr); if (Error::None != status) { diff --git a/test/unit/gatewayTest.cpp b/test/unit/gatewayTest.cpp index 693117c..67b519e 100644 --- a/test/unit/gatewayTest.cpp +++ b/test/unit/gatewayTest.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -1478,6 +1479,55 @@ TEST_F(GatewayUTest, LegacyUnsubscribeIteratesPastNonMatchingEntry) EXPECT_EQ(err, Firebolt::Error::None); } +// --------------------------------------------------------------------------- +// Test name: GatewayUTest.NotificationWorkerContinuesAfterCallbackException +// Covers: src/gateway.cpp: notification worker for-loop catch block — the +// worker must continue dispatching remaining callbacks in the same +// notification batch even when one callback throws std::exception. +// Regression: before the try/catch was added, the exception would +// escape the thread entry point and call std::terminate. +// Scenario type: regression +// --------------------------------------------------------------------------- +TEST_F(GatewayUTest, NotificationWorkerContinuesAfterCallbackException) +{ + IGateway& gateway = connectAndWait(); + + // Callback A intentionally throws — exercises catch(const std::exception&) + auto onEventA = [](void*, const nlohmann::json&) { throw std::runtime_error("test: simulated callback exception"); }; + int cbA = 0; + + // Callback B signals via a promise — confirms the worker reached it after A threw + std::promise deliveredPromise; + auto deliveredFuture = deliveredPromise.get_future(); + auto onEventB = [](void* usercb, const nlohmann::json&) + { static_cast*>(usercb)->set_value(true); }; + + // Both subscribe to the same event so they land in the same notification.callbacks vector + Firebolt::Error err = gateway.subscribe("test.onWorkerContinues", onEventA, &cbA); + EXPECT_EQ(err, Firebolt::Error::None); + err = gateway.subscribe("test.onWorkerContinues", onEventB, &deliveredPromise); + EXPECT_EQ(err, Firebolt::Error::None); + + // Server fires the event once + m_onMessageAction = [](server* s, connection_hdl hdl) + { + nlohmann::json eventMsg; + eventMsg["jsonrpc"] = "2.0"; + eventMsg["method"] = "test.onWorkerContinues"; + eventMsg["params"] = {{"fired", true}}; + s->send(hdl, eventMsg.dump(), websocketpp::frame::opcode::text); + }; + gateway.send("dummy.message", {}); + + // Callback B must still fire — worker continued the loop despite A throwing + auto status = deliveredFuture.wait_for(std::chrono::seconds(2)); + ASSERT_EQ(status, std::future_status::ready) << "Notification worker stopped after exception in callback A"; + EXPECT_TRUE(deliveredFuture.get()); + + gateway.unsubscribe("test.onWorkerContinues", &cbA); + gateway.unsubscribe("test.onWorkerContinues", &deliveredPromise); +} + // Regression test: disconnect() must cancel all pending requests so that calling // threads blocked on future.get() unblock immediately with NotConnected rather // than hanging indefinitely. diff --git a/test/unit/helperTest.cpp b/test/unit/helperTest.cpp index b89cd65..e901815 100644 --- a/test/unit/helperTest.cpp +++ b/test/unit/helperTest.cpp @@ -622,11 +622,13 @@ TEST_F(HelperUTest, WeakPtrGuard_NotificationDeliveredWhileSubscribed) void* capturedUsercb = nullptr; EXPECT_CALL(mockGateway, subscribe("test.onEvent", _, _)) - .WillOnce([&](const std::string&, Firebolt::Transport::EventCallback cb, void* ucb) { - capturedCallback = cb; - capturedUsercb = ucb; - return Error::None; - }); + .WillOnce( + [&](const std::string&, Firebolt::Transport::EventCallback cb, void* ucb) + { + capturedCallback = std::move(cb); + capturedUsercb = ucb; + return Error::None; + }); std::promise deliveredValue; auto future = deliveredValue.get_future(); @@ -665,11 +667,13 @@ TEST_F(HelperUTest, WeakPtrGuard_NotificationDroppedAfterUnsubscribeAll) void* capturedUsercb = nullptr; EXPECT_CALL(mockGateway, subscribe("tts.onSpeechInterrupted", _, _)) - .WillOnce([&](const std::string&, Firebolt::Transport::EventCallback cb, void* ucb) { - capturedCallback = cb; - capturedUsercb = ucb; - return Error::None; - }); + .WillOnce( + [&](const std::string&, Firebolt::Transport::EventCallback cb, void* ucb) + { + capturedCallback = std::move(cb); + capturedUsercb = ucb; + return Error::None; + }); bool notificationFired = false; std::function notification = [¬ificationFired](int) { notificationFired = true; }; @@ -705,11 +709,13 @@ TEST_F(HelperUTest, WeakPtrGuard_NotificationDroppedAfterUnsubscribeById) void* capturedUsercb = nullptr; EXPECT_CALL(mockGateway, subscribe("test.onEvent", _, _)) - .WillOnce([&](const std::string&, Firebolt::Transport::EventCallback cb, void* ucb) { - capturedCallback = cb; - capturedUsercb = ucb; - return Error::None; - }); + .WillOnce( + [&](const std::string&, Firebolt::Transport::EventCallback cb, void* ucb) + { + capturedCallback = std::move(cb); + capturedUsercb = ucb; + return Error::None; + }); bool notificationFired = false; std::function notification = [¬ificationFired](int) { notificationFired = true; }; From eeacd529b1118796550a299f5f9e2f796e6f6b7e Mon Sep 17 00:00:00 2001 From: swethasukumarr Date: Mon, 20 Jul 2026 15:18:40 -0400 Subject: [PATCH 4/5] Catch any_cast errors in onPropertyChangedCallback --- include/firebolt/helpers.h | 4 ++-- test/unit/helperTest.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/firebolt/helpers.h b/include/firebolt/helpers.h index 0910b66..51eef36 100644 --- a/include/firebolt/helpers.h +++ b/include/firebolt/helpers.h @@ -40,10 +40,10 @@ template void onPropertyChangedCallback(void* subscriptionDataPtr, const nlohmann::json& jsonResponse) { SubscriptionData* subscriptionData = reinterpret_cast(subscriptionDataPtr); - auto notifier = std::any_cast>(subscriptionData->notification); - JsonType jsonType; try { + auto notifier = std::any_cast>(subscriptionData->notification); + JsonType jsonType; jsonType.fromJson(jsonResponse); if constexpr (sizeof...(Args) > 1) { diff --git a/test/unit/helperTest.cpp b/test/unit/helperTest.cpp index e901815..73792cb 100644 --- a/test/unit/helperTest.cpp +++ b/test/unit/helperTest.cpp @@ -640,7 +640,7 @@ TEST_F(HelperUTest, WeakPtrGuard_NotificationDeliveredWhileSubscribed) ASSERT_TRUE(subResult); ASSERT_TRUE(capturedCallback) << "wrappedCallback must have been captured"; - // Worker thread dispatches notification while subscription is still alive + // Simulate the worker thread dispatching a notification while the subscription is still alive capturedCallback(capturedUsercb, nlohmann::json{{"value", 42}}); auto status = future.wait_for(std::chrono::seconds(1)); @@ -688,8 +688,8 @@ TEST_F(HelperUTest, WeakPtrGuard_NotificationDroppedAfterUnsubscribeAll) EXPECT_CALL(mockGateway, unsubscribe("tts.onSpeechInterrupted", _)).WillOnce(Return(Error::None)); helper.unsubscribeAll(this); - // Platform sends onSpeechInterrupted — notification was already queued in-flight - // Replay the race: call the wrappedCallback with the now-stale usercb address. + // Simulate the platform sending onSpeechInterrupted after unsubscribeAll() — replay the race by calling + // the wrappedCallback with the now-stale usercb address. ASSERT_TRUE(capturedCallback) << "wrappedCallback must have been captured"; capturedCallback(capturedUsercb, nlohmann::json{{"value", 1}}); From 76ce5bcdbec4b684d561267e740486b0882b600b Mon Sep 17 00:00:00 2001 From: swethasukumarr Date: Mon, 20 Jul 2026 15:29:24 -0400 Subject: [PATCH 5/5] Address copilot comments --- include/firebolt/helpers.h | 7 ++++++- src/gateway.cpp | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/firebolt/helpers.h b/include/firebolt/helpers.h index 51eef36..f4a1b44 100644 --- a/include/firebolt/helpers.h +++ b/include/firebolt/helpers.h @@ -54,9 +54,14 @@ void onPropertyChangedCallback(void* subscriptionDataPtr, const nlohmann::json& notifier(jsonType.value()); } } + catch (const std::bad_any_cast& e) + { + FIREBOLT_LOG_ERROR("Event", "Notification type mismatch for event '%s': %s", + subscriptionData->eventName.c_str(), e.what()); + } catch (const std::exception& e) { - FIREBOLT_LOG_ERROR("Event", "Cannot parse event data for event %s, payload: %s", + FIREBOLT_LOG_ERROR("Event", "Cannot parse event data for event '%s', payload: %s", subscriptionData->eventName.c_str(), jsonResponse.dump().c_str()); } } diff --git a/src/gateway.cpp b/src/gateway.cpp index c963338..3017ce6 100644 --- a/src/gateway.cpp +++ b/src/gateway.cpp @@ -369,7 +369,7 @@ class Server catch (const std::bad_any_cast& e) { FIREBOLT_LOG_ERROR("Gateway", - "[notification-worker] bad_any_cast dispatching event='%s': %s — " + "[notification-worker] bad_any_cast dispatching event='%s': %s - " "notification type does not match the registered callback signature", callback.eventName.c_str(), e.what()); }