diff --git a/plugin/tensorboard_plugin_profile/convert/raw_to_tool_data.py b/plugin/tensorboard_plugin_profile/convert/raw_to_tool_data.py
index 2c32fef3..1c1db8c5 100644
--- a/plugin/tensorboard_plugin_profile/convert/raw_to_tool_data.py
+++ b/plugin/tensorboard_plugin_profile/convert/raw_to_tool_data.py
@@ -73,7 +73,7 @@ def xspace_to_tool_data(
     tool: A string of tool name.
     params: user input parameters.
     xspace_wrapper_func: A callable that takes a list of strings and a tool and
-      returns the raw data.
+      returns the raw data. If failed, raw data contains the error message.
 
   Returns:
     Returns a string of tool data and the content type for the response.
@@ -152,6 +152,12 @@ def xspace_to_tool_data(
     if success:
       data = raw_data
       content_type = 'text/html'
+    else:
+      # TODO(tf-profiler) Handle errors for other tools as well,
+      # to pass along the error message to client
+      if isinstance(raw_data, bytes):
+        raw_data = raw_data.decode('utf-8')
+      raise ValueError(raw_data)
   elif tool == 'memory_viewer':
     options = {'module_name': params.get('host')}
     raw_data, success = xspace_wrapper_func(xspace_paths, tool, options)
diff --git a/plugin/tensorboard_plugin_profile/profile_plugin.py b/plugin/tensorboard_plugin_profile/profile_plugin.py
index d936a9ac..20c6999a 100644
--- a/plugin/tensorboard_plugin_profile/profile_plugin.py
+++ b/plugin/tensorboard_plugin_profile/profile_plugin.py
@@ -478,7 +478,7 @@ def static_file_route(self, request):
     try:
       contents = self._read_static_file_impl(filename)
     except IOError:
-      return respond('404 Not Found', 'text/plain', code=404)
+      return respond('Fail to read the files.', 'text/plain', code=404)
     return respond(contents, mimetype)
 
   @wrappers.Request.application
@@ -622,14 +622,23 @@ def data_impl(self, request):
         except tf.errors.OpError as e:
           logger.warning('Cannot read asset directory: %s, OpError %s', run_dir,
                          e)
+          raise IOError(
+              'Cannot read asset directory: %s, OpError %s' % (run_dir, e)
+          ) from e
       else:
         asset_paths = [asset_path]
 
       try:
         data, content_type = convert.xspace_to_tool_data(
             asset_paths, tool, params)
-      except AttributeError:
+      except AttributeError as e:
         logger.warning('XPlane converters are available after Tensorflow 2.4')
+        raise AttributeError(
+            'XPlane converters are available after Tensorflow 2.4'
+        ) from e
+      except ValueError as e:
+        logger.warning('XPlane convert to tool data failed as %s', e)
+        raise e
       return data, content_type, content_encoding
 
     raw_data = None
@@ -650,10 +659,18 @@ def data_impl(self, request):
   def data_route(self, request):
     # params
     #   request: XMLHTTPRequest.
-    data, content_type, content_encoding = self.data_impl(request)
-    if data is None:
-      return respond('404 Not Found', 'text/plain', code=404)
-    return respond(data, content_type, content_encoding=content_encoding)
+    try:
+      data, content_type, content_encoding = self.data_impl(request)
+      if data is None:
+        return respond('No Data', 'text/plain', code=404)
+      return respond(data, content_type, content_encoding=content_encoding)
+    # Data fetch error handler
+    except AttributeError as e:
+      return respond(str(e), 'text/plain', code=500)
+    except ValueError as e:
+      return respond(str(e), 'text/plain', code=500)
+    except IOError as e:
+      return respond(str(e), 'text/plain', code=500)
 
   @wrappers.Request.application
   def capture_route(self, request):