diff --git a/daemon/core/gui/coreclient.py b/daemon/core/gui/coreclient.py
index 11030eda..f99147be 100644
--- a/daemon/core/gui/coreclient.py
+++ b/daemon/core/gui/coreclient.py
@@ -335,7 +335,7 @@ class CoreClient:
             self.parse_metadata(response.config)
 
         except grpc.RpcError as e:
-            self.app.after(0, show_grpc_error, e)
+            self.app.after(0, show_grpc_error, e, self.app, self.app)
 
         # update ui to represent current state
         self.app.after(0, self.app.joined_session_update)
@@ -423,16 +423,20 @@ class CoreClient:
             )
             self.join_session(response.session_id, query_location=False)
         except grpc.RpcError as e:
-            self.app.after(0, show_grpc_error, e)
+            self.app.after(0, show_grpc_error, e, self.app, self.app)
 
-    def delete_session(self, session_id: int = None):
+    def delete_session(self, session_id: int = None, parent_frame=None):
         if session_id is None:
             session_id = self.session_id
         try:
             response = self.client.delete_session(session_id)
             logging.info("deleted session(%s), Result: %s", session_id, response)
         except grpc.RpcError as e:
-            self.app.after(0, show_grpc_error, e)
+            # use the right master widget so the error dialog displays right on top of it
+            master = self.app
+            if parent_frame:
+                master = parent_frame
+            self.app.after(0, show_grpc_error, e, master, self.app)
 
     def set_up(self):
         """
@@ -468,7 +472,7 @@ class CoreClient:
                 x.node_type: set(x.services) for x in response.defaults
             }
         except grpc.RpcError as e:
-            self.app.after(0, show_grpc_error, e)
+            self.app.after(0, show_grpc_error, e, self.app, self.app)
             self.app.close()
 
     def edit_node(self, core_node: core_pb2.Node):
@@ -477,7 +481,7 @@ class CoreClient:
                 self.session_id, core_node.id, core_node.position, source=GUI_SOURCE
             )
         except grpc.RpcError as e:
-            self.app.after(0, show_grpc_error, e)
+            self.app.after(0, show_grpc_error, e, self.app, self.app)
 
     def start_session(self) -> core_pb2.StartSessionResponse:
         nodes = [x.core_node for x in self.canvas_nodes.values()]
@@ -521,7 +525,7 @@ class CoreClient:
             if response.result:
                 self.set_metadata()
         except grpc.RpcError as e:
-            self.app.after(0, show_grpc_error, e)
+            self.app.after(0, show_grpc_error, e, self.app, self.app)
         return response
 
     def stop_session(self, session_id: int = None) -> core_pb2.StartSessionResponse:
@@ -532,7 +536,7 @@ class CoreClient:
             response = self.client.stop_session(session_id)
             logging.info("stopped session(%s), result: %s", session_id, response)
         except grpc.RpcError as e:
-            self.app.after(0, show_grpc_error, e)
+            self.app.after(0, show_grpc_error, e, self.app, self.app)
         return response
 
     def show_mobility_players(self):
@@ -577,7 +581,7 @@ class CoreClient:
             logging.info("launching terminal %s", cmd)
             os.system(cmd)
         except grpc.RpcError as e:
-            self.app.after(0, show_grpc_error, e)
+            self.app.after(0, show_grpc_error, e, self.app, self.app)
 
     def save_xml(self, file_path: str):
         """
@@ -590,7 +594,7 @@ class CoreClient:
             response = self.client.save_xml(self.session_id, file_path)
             logging.info("saved xml file %s, result: %s", file_path, response)
         except grpc.RpcError as e:
-            self.app.after(0, show_grpc_error, e)
+            self.app.after(0, show_grpc_error, e, self.app, self.app)
 
     def open_xml(self, file_path: str):
         """
@@ -601,7 +605,7 @@ class CoreClient:
             logging.info("open xml file %s, response: %s", file_path, response)
             self.join_session(response.session_id)
         except grpc.RpcError as e:
-            self.app.after(0, show_grpc_error, e)
+            self.app.after(0, show_grpc_error, e, self.app, self.app)
 
     def get_node_service(
         self, node_id: int, service_name: str
diff --git a/daemon/core/gui/data/icons/error.png b/daemon/core/gui/data/icons/error.png
new file mode 100644
index 00000000..d73d1dd4
Binary files /dev/null and b/daemon/core/gui/data/icons/error.png differ
diff --git a/daemon/core/gui/dialogs/configserviceconfig.py b/daemon/core/gui/dialogs/configserviceconfig.py
index f92d23bb..3aaac1a4 100644
--- a/daemon/core/gui/dialogs/configserviceconfig.py
+++ b/daemon/core/gui/dialogs/configserviceconfig.py
@@ -65,8 +65,13 @@ class ConfigServiceConfigDialog(Dialog):
         self.config_frame = None
         self.default_config = None
         self.config = None
+
+        self.has_error = False
+
         self.load()
-        self.draw()
+
+        if not self.has_error:
+            self.draw()
 
     def load(self):
         try:
@@ -106,7 +111,8 @@ class ConfigServiceConfigDialog(Dialog):
                 self.modified_files.add(file)
                 self.temp_service_files[file] = data
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            self.has_error = True
+            show_grpc_error(e, self.app, self.app)
 
     def draw(self):
         self.top.columnconfigure(0, weight=1)
@@ -327,7 +333,7 @@ class ConfigServiceConfigDialog(Dialog):
             all_current = current_listbox.get(0, tk.END)
             current_listbox.itemconfig(all_current.index(self.service_name), bg="green")
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            show_grpc_error(e, self.top, self.app)
         self.destroy()
 
     def handle_template_changed(self, event: tk.Event):
diff --git a/daemon/core/gui/dialogs/emaneconfig.py b/daemon/core/gui/dialogs/emaneconfig.py
index 1bcdd78b..e5f403fe 100644
--- a/daemon/core/gui/dialogs/emaneconfig.py
+++ b/daemon/core/gui/dialogs/emaneconfig.py
@@ -65,14 +65,16 @@ class EmaneModelDialog(Dialog):
         self.model = f"emane_{model}"
         self.interface = interface
         self.config_frame = None
+        self.has_error = False
         try:
             self.config = self.app.core.get_emane_model_config(
                 self.node.id, self.model, self.interface
             )
+            self.draw()
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            show_grpc_error(e, self.app, self.app)
+            self.has_error = True
             self.destroy()
-        self.draw()
 
     def draw(self):
         self.top.columnconfigure(0, weight=1)
@@ -225,7 +227,8 @@ class EmaneConfigDialog(Dialog):
         dialog = EmaneModelDialog(
             self, self.app, self.canvas_node.core_node, model_name
         )
-        dialog.show()
+        if not dialog.has_error:
+            dialog.show()
 
     def emane_model_change(self, event: tk.Event):
         """
diff --git a/daemon/core/gui/dialogs/mobilityconfig.py b/daemon/core/gui/dialogs/mobilityconfig.py
index 18e62a17..f7192ca4 100644
--- a/daemon/core/gui/dialogs/mobilityconfig.py
+++ b/daemon/core/gui/dialogs/mobilityconfig.py
@@ -29,12 +29,14 @@ class MobilityConfigDialog(Dialog):
         self.canvas_node = canvas_node
         self.node = canvas_node.core_node
         self.config_frame = None
+        self.has_error = False
         try:
             self.config = self.app.core.get_mobility_config(self.node.id)
+            self.draw()
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            self.has_error = True
+            show_grpc_error(e, self.app, self.app)
             self.destroy()
-        self.draw()
 
     def draw(self):
         self.top.columnconfigure(0, weight=1)
diff --git a/daemon/core/gui/dialogs/mobilityplayer.py b/daemon/core/gui/dialogs/mobilityplayer.py
index 873a2b37..dd2e9f9a 100644
--- a/daemon/core/gui/dialogs/mobilityplayer.py
+++ b/daemon/core/gui/dialogs/mobilityplayer.py
@@ -153,7 +153,7 @@ class MobilityPlayerDialog(Dialog):
                 session_id, self.node.id, MobilityAction.START
             )
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            show_grpc_error(e, self.top, self.app)
 
     def click_pause(self):
         self.set_pause()
@@ -163,7 +163,7 @@ class MobilityPlayerDialog(Dialog):
                 session_id, self.node.id, MobilityAction.PAUSE
             )
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            show_grpc_error(e, self.top, self.app)
 
     def click_stop(self):
         self.set_stop()
@@ -173,4 +173,4 @@ class MobilityPlayerDialog(Dialog):
                 session_id, self.node.id, MobilityAction.STOP
             )
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            show_grpc_error(e, self.top, self.app)
diff --git a/daemon/core/gui/dialogs/nodeconfigservice.py b/daemon/core/gui/dialogs/nodeconfigservice.py
index 1230cede..e41290ed 100644
--- a/daemon/core/gui/dialogs/nodeconfigservice.py
+++ b/daemon/core/gui/dialogs/nodeconfigservice.py
@@ -124,7 +124,8 @@ class NodeConfigServiceDialog(Dialog):
                 service_name=self.current.listbox.get(current_selection[0]),
                 node_id=self.node_id,
             )
-            dialog.show()
+            if not dialog.has_error:
+                dialog.show()
         else:
             messagebox.showinfo(
                 "Node service configuration", "Select a service to configure"
diff --git a/daemon/core/gui/dialogs/nodeservice.py b/daemon/core/gui/dialogs/nodeservice.py
index c61983f7..f2fe1db2 100644
--- a/daemon/core/gui/dialogs/nodeservice.py
+++ b/daemon/core/gui/dialogs/nodeservice.py
@@ -140,7 +140,12 @@ class NodeServiceDialog(Dialog):
                 service_name=self.current.listbox.get(current_selection[0]),
                 node_id=self.node_id,
             )
-            dialog.show()
+
+            # if error occurred when creating ServiceConfigDialog, don't show the dialog
+            if not dialog.has_error:
+                dialog.show()
+            else:
+                dialog.destroy()
         else:
             messagebox.showinfo(
                 "Node service configuration", "Select a service to configure"
diff --git a/daemon/core/gui/dialogs/serviceconfig.py b/daemon/core/gui/dialogs/serviceconfig.py
index 804e7e3f..e8c13019 100644
--- a/daemon/core/gui/dialogs/serviceconfig.py
+++ b/daemon/core/gui/dialogs/serviceconfig.py
@@ -64,10 +64,14 @@ class ServiceConfigDialog(Dialog):
         self.original_service_files = {}
         self.temp_service_files = {}
         self.modified_files = set()
-        self.load()
-        self.draw()
 
-    def load(self):
+        self.has_error = False
+
+        self.load()
+        if not self.has_error:
+            self.draw()
+
+    def load(self) -> bool:
         try:
             self.app.core.create_nodes_and_links()
             default_config = self.app.core.get_node_service(
@@ -109,7 +113,8 @@ class ServiceConfigDialog(Dialog):
                 for file, data in file_configs[self.node_id][self.service_name].items():
                     self.temp_service_files[file] = data
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            self.has_error = True
+            show_grpc_error(e, self.master, self.app)
 
     def draw(self):
         self.top.columnconfigure(0, weight=1)
@@ -444,7 +449,7 @@ class ServiceConfigDialog(Dialog):
             all_current = current_listbox.get(0, tk.END)
             current_listbox.itemconfig(all_current.index(self.service_name), bg="green")
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            show_grpc_error(e, self.top, self.app)
         self.destroy()
 
     def display_service_file_data(self, event: tk.Event):
diff --git a/daemon/core/gui/dialogs/sessionoptions.py b/daemon/core/gui/dialogs/sessionoptions.py
index ffd61340..a3f738a7 100644
--- a/daemon/core/gui/dialogs/sessionoptions.py
+++ b/daemon/core/gui/dialogs/sessionoptions.py
@@ -17,8 +17,10 @@ class SessionOptionsDialog(Dialog):
     def __init__(self, master: "Application", app: "Application"):
         super().__init__(master, app, "Session Options", modal=True)
         self.config_frame = None
+        self.has_error = False
         self.config = self.get_config()
-        self.draw()
+        if not self.has_error:
+            self.draw()
 
     def get_config(self):
         try:
@@ -26,7 +28,8 @@ class SessionOptionsDialog(Dialog):
             response = self.app.core.client.get_session_options(session_id)
             return response.config
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            self.has_error = True
+            show_grpc_error(e, self.app, self.app)
             self.destroy()
 
     def draw(self):
@@ -53,5 +56,5 @@ class SessionOptionsDialog(Dialog):
             response = self.app.core.client.set_session_options(session_id, config)
             logging.info("saved session config: %s", response)
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            show_grpc_error(e, self.top, self.app)
         self.destroy()
diff --git a/daemon/core/gui/dialogs/sessions.py b/daemon/core/gui/dialogs/sessions.py
index 3af76c52..e540a3ca 100644
--- a/daemon/core/gui/dialogs/sessions.py
+++ b/daemon/core/gui/dialogs/sessions.py
@@ -25,8 +25,10 @@ class SessionsDialog(Dialog):
         self.selected = False
         self.selected_id = None
         self.tree = None
+        self.has_error = False
         self.sessions = self.get_sessions()
-        self.draw()
+        if not self.has_error:
+            self.draw()
 
     def get_sessions(self) -> Iterable[core_pb2.SessionSummary]:
         try:
@@ -34,7 +36,8 @@ class SessionsDialog(Dialog):
             logging.info("sessions: %s", response)
             return response.sessions
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            show_grpc_error(e, self.app, self.app)
+            self.has_error = True
             self.destroy()
 
     def draw(self):
@@ -211,7 +214,7 @@ class SessionsDialog(Dialog):
         item = self.tree.selection()
         if item:
             sid = int(self.tree.item(item, "text"))
-            self.app.core.delete_session(sid)
+            self.app.core.delete_session(sid, self.top)
             self.tree.delete(item[0])
             if sid == self.app.core.session_id:
                 self.click_new()
diff --git a/daemon/core/gui/dialogs/wlanconfig.py b/daemon/core/gui/dialogs/wlanconfig.py
index 264b9e2e..d6da667e 100644
--- a/daemon/core/gui/dialogs/wlanconfig.py
+++ b/daemon/core/gui/dialogs/wlanconfig.py
@@ -27,12 +27,14 @@ class WlanConfigDialog(Dialog):
         self.canvas_node = canvas_node
         self.node = canvas_node.core_node
         self.config_frame = None
+        self.has_error = False
         try:
             self.config = self.app.core.get_wlan_config(self.node.id)
+            self.draw()
         except grpc.RpcError as e:
-            show_grpc_error(e)
+            show_grpc_error(e, self.app, self.app)
+            self.has_error = True
             self.destroy()
-        self.draw()
 
     def draw(self):
         self.top.columnconfigure(0, weight=1)
diff --git a/daemon/core/gui/errors.py b/daemon/core/gui/errors.py
index 18e025db..b6152489 100644
--- a/daemon/core/gui/errors.py
+++ b/daemon/core/gui/errors.py
@@ -1,12 +1,38 @@
-from tkinter import messagebox
+from tkinter import ttk
 from typing import TYPE_CHECKING
 
+from core.gui.dialogs.dialog import Dialog
+from core.gui.images import ImageEnum, Images
+from core.gui.widgets import CodeText
+
 if TYPE_CHECKING:
     import grpc
+    from core.gui.app import Application
 
 
-def show_grpc_error(e: "grpc.RpcError"):
+class ErrorDialog(Dialog):
+    def __init__(self, master, app: "Application", title: str, details: str):
+        super().__init__(master, app, title, modal=True)
+        self.error_message = None
+        self.details = details
+        self.draw()
+
+    def draw(self):
+        self.top.columnconfigure(0, weight=1)
+        self.top.rowconfigure(0, weight=1)
+        image = Images.get(ImageEnum.ERROR, 36)
+        label = ttk.Label(self.top, image=image)
+        label.image = image
+        label.grid(row=0, column=0)
+        self.error_message = CodeText(self.top)
+        self.error_message.text.insert("1.0", self.details)
+        self.error_message.text.config(state="disabled")
+        self.error_message.grid(row=1, column=0, sticky="nsew")
+
+
+def show_grpc_error(e: "grpc.RpcError", master, app: "Application"):
     title = [x.capitalize() for x in e.code().name.lower().split("_")]
     title = " ".join(title)
     title = f"GRPC {title}"
-    messagebox.showerror(title, e.details())
+    dialog = ErrorDialog(master, app, title, e.details())
+    dialog.show()
diff --git a/daemon/core/gui/graph/node.py b/daemon/core/gui/graph/node.py
index ff33d7e5..dbd8827b 100644
--- a/daemon/core/gui/graph/node.py
+++ b/daemon/core/gui/graph/node.py
@@ -163,7 +163,7 @@ class CanvasNode:
                 output = self.app.core.run(self.core_node.id)
                 self.tooltip.text.set(output)
             except grpc.RpcError as e:
-                show_grpc_error(e)
+                show_grpc_error(e, self.app, self.app)
 
     def on_leave(self, event: tk.Event):
         self.tooltip.on_leave(event)
@@ -238,12 +238,14 @@ class CanvasNode:
     def show_wlan_config(self):
         self.canvas.context = None
         dialog = WlanConfigDialog(self.app, self.app, self)
-        dialog.show()
+        if not dialog.has_error:
+            dialog.show()
 
     def show_mobility_config(self):
         self.canvas.context = None
         dialog = MobilityConfigDialog(self.app, self.app, self)
-        dialog.show()
+        if not dialog.has_error:
+            dialog.show()
 
     def show_mobility_player(self):
         self.canvas.context = None
diff --git a/daemon/core/gui/images.py b/daemon/core/gui/images.py
index 6bf23e24..cd472764 100644
--- a/daemon/core/gui/images.py
+++ b/daemon/core/gui/images.py
@@ -89,3 +89,4 @@ class ImageEnum(Enum):
     DELETE = "delete"
     SHUTDOWN = "shutdown"
     CANCEL = "cancel"
+    ERROR = "error"
diff --git a/daemon/core/gui/menuaction.py b/daemon/core/gui/menuaction.py
index 92657cc5..95699d4c 100644
--- a/daemon/core/gui/menuaction.py
+++ b/daemon/core/gui/menuaction.py
@@ -9,6 +9,8 @@ import webbrowser
 from tkinter import filedialog, messagebox
 from typing import TYPE_CHECKING
 
+import grpc
+
 from core.gui.appconfig import XMLS_PATH
 from core.gui.dialogs.about import AboutDialog
 from core.gui.dialogs.canvassizeandscale import SizeAndScaleDialog
@@ -34,12 +36,18 @@ class MenuAction:
         self.app = app
         self.canvas = app.canvas
 
-    def cleanup_old_session(self, session_id):
-        response = self.app.core.stop_session()
-        self.app.core.delete_session(session_id)
-        logging.info(
-            "Stop session(%s) and delete it, result: %s", session_id, response.result
-        )
+    def cleanup_old_session(self, session_id: int):
+        try:
+            res = self.app.core.client.get_session(session_id)
+            logging.debug("retrieve session(%s), %s", session_id, res)
+            stop_response = self.app.core.stop_session()
+            logging.debug("stop session(%s), result: %s", session_id, stop_response)
+            delete_response = self.app.core.delete_session(session_id)
+            logging.debug(
+                "deleted session(%s), result: %s", session_id, delete_response
+            )
+        except grpc.RpcError:
+            logging.debug("session is not alive")
 
     def prompt_save_running_session(self, quitapp: bool = False):
         """
@@ -125,7 +133,8 @@ class MenuAction:
     def session_options(self):
         logging.debug("Click options")
         dialog = SessionOptionsDialog(self.app, self.app)
-        dialog.show()
+        if not dialog.has_error:
+            dialog.show()
 
     def session_change_sessions(self):
         logging.debug("Click change sessions")
diff --git a/daemon/core/gui/toolbar.py b/daemon/core/gui/toolbar.py
index 8298cafb..1df24993 100644
--- a/daemon/core/gui/toolbar.py
+++ b/daemon/core/gui/toolbar.py
@@ -3,7 +3,7 @@ import time
 import tkinter as tk
 from enum import Enum
 from functools import partial
-from tkinter import messagebox, ttk
+from tkinter import ttk
 from typing import TYPE_CHECKING, Callable
 
 from core.api.grpc import core_pb2
@@ -302,9 +302,6 @@ class Toolbar(ttk.Frame):
             self.set_runtime()
             self.app.core.set_metadata()
             self.app.core.show_mobility_players()
-        else:
-            message = "\n".join(response.exceptions)
-            messagebox.showerror("Start Error", message)
 
     def set_runtime(self):
         self.runtime_frame.tkraise()
@@ -490,8 +487,6 @@ class Toolbar(ttk.Frame):
         message = f"Stopped in {total:.3f} seconds"
         self.app.statusbar.set_status(message)
         self.app.canvas.stopped_session()
-        if not response.result:
-            messagebox.showerror("Stop Error", "Errors stopping session")
 
     def update_annotation(
         self, image: "ImageTk.PhotoImage", shape_type: ShapeType, image_enum
diff --git a/daemon/core/services/frr.py b/daemon/core/services/frr.py
index 472eae51..52429b26 100644
--- a/daemon/core/services/frr.py
+++ b/daemon/core/services/frr.py
@@ -208,6 +208,9 @@ bootfrr()
     fi
 
     bootdaemon "zebra"
+    if grep -q "^ip route " $FRR_CONF; then
+        bootdaemon "staticd"
+    fi
     for r in rip ripng ospf6 ospf bgp babel; do
         if grep -q "^router \\<${r}\\>" $FRR_CONF; then
             bootdaemon "${r}d"