Better error reporting
This commit is contained in:
@@ -56,11 +56,14 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
try:
|
||||
browsersteps_start_session['browser'] = io_interface_context.chromium.connect_over_cdp(base_url)
|
||||
except Exception as e:
|
||||
if 'ECONNREFUSED' in str(e):
|
||||
return make_response('Unable to start the Playwright Browser session, is it running?', 401)
|
||||
error_message = str(e)
|
||||
if 'ECONNREFUSED' in error_message:
|
||||
return make_response('Could not connect to the virtual browser. Please check if the browser service is running.', 401)
|
||||
elif "'Response' object is not subscriptable" in error_message:
|
||||
return make_response('Could not connect to the virtual browser. Connection to browser failed with invalid response.', 401)
|
||||
else:
|
||||
# Other errors, bad URL syntax, bad reply etc
|
||||
return make_response(str(e), 401)
|
||||
return make_response(f'Could not connect to the virtual browser: {error_message.splitlines()[0]}', 401)
|
||||
|
||||
proxy_id = datastore.get_preferred_proxy_for_watch(uuid=watch_uuid)
|
||||
proxy = None
|
||||
@@ -155,7 +158,7 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
return make_response('No browsersteps_session_id specified', 500)
|
||||
|
||||
if not browsersteps_sessions.get(browsersteps_session_id):
|
||||
return make_response('No session exists under that ID', 500)
|
||||
return make_response('Could not connect to the virtual browser. The session has expired or does not exist.', 401)
|
||||
|
||||
is_last_step = False
|
||||
# Actions - step/apply/etc, do the thing and return state
|
||||
@@ -174,9 +177,17 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
optional_value=step_optional_value)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Exception when calling step operation {step_operation} {str(e)}")
|
||||
# Try to find something of value to give back to the user
|
||||
return make_response(str(e).splitlines()[0], 401)
|
||||
error_message = str(e)
|
||||
logger.error(f"Exception when calling step operation {step_operation} {error_message}")
|
||||
|
||||
# Provide user-friendly error messages
|
||||
if "'Response' object is not subscriptable" in error_message:
|
||||
return make_response('Could not connect to the virtual browser. Connection was lost or failed.', 401)
|
||||
elif "timed out" in error_message.lower() or "timeout" in error_message.lower():
|
||||
return make_response('Browser operation timed out. The page might be loading too slowly.', 401)
|
||||
else:
|
||||
# Try to find something of value to give back to the user
|
||||
return make_response(f'Browser operation failed: {error_message.splitlines()[0]}', 401)
|
||||
|
||||
|
||||
# if not this_session.page:
|
||||
@@ -194,9 +205,19 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
watch.save_xpath_data(data=xpath_data)
|
||||
|
||||
except playwright._impl._api_types.Error as e:
|
||||
return make_response("Browser session ran out of time :( Please reload this page."+str(e), 401)
|
||||
error_message = str(e)
|
||||
if "'Response' object is not subscriptable" in error_message:
|
||||
return make_response("Could not connect to the virtual browser. Connection was lost or failed.", 401)
|
||||
elif "session closed" in error_message.lower() or "target closed" in error_message.lower():
|
||||
return make_response("Browser session has closed. Please reload the page to start a new session.", 401)
|
||||
else:
|
||||
return make_response("Browser session ran out of time. Please reload this page.", 401)
|
||||
except Exception as e:
|
||||
return make_response("Error fetching screenshot and element data - " + str(e), 401)
|
||||
error_message = str(e)
|
||||
if "'Response' object is not subscriptable" in error_message:
|
||||
return make_response("Could not connect to the virtual browser. Connection was lost or failed.", 401)
|
||||
else:
|
||||
return make_response("Error fetching screenshot and element data: " + error_message.splitlines()[0], 401)
|
||||
|
||||
# SEND THIS BACK TO THE BROWSER
|
||||
|
||||
|
||||
@@ -65,24 +65,41 @@ class steppable_browser_interface():
|
||||
"""Safely execute a page operation with error handling"""
|
||||
if self.page is None:
|
||||
logger.warning("Attempted operation on None page object")
|
||||
return default_return
|
||||
raise Exception("Could not connect to the virtual browser. Session may have expired.")
|
||||
|
||||
try:
|
||||
return operation_fn()
|
||||
except Exception as e:
|
||||
logger.debug(f"Page operation failed: {str(e)}")
|
||||
error_message = str(e)
|
||||
logger.debug(f"Page operation failed: {error_message}")
|
||||
|
||||
# Try to reclaim memory if possible
|
||||
try:
|
||||
self.page.request_gc()
|
||||
except:
|
||||
pass
|
||||
return default_return
|
||||
|
||||
# Provide more user-friendly error messages for common issues
|
||||
if "'Response' object is not subscriptable" in error_message:
|
||||
raise Exception("Could not connect to the virtual browser. Connection was lost or failed.")
|
||||
elif "session closed" in error_message.lower() or "target closed" in error_message.lower():
|
||||
raise Exception("Browser session has closed. Please reload the page to start a new session.")
|
||||
elif "timeout" in error_message.lower():
|
||||
raise Exception("Browser operation timed out. The page might be loading too slowly.")
|
||||
else:
|
||||
# Re-raise the original exception to be handled by the caller
|
||||
raise
|
||||
|
||||
# Convert and perform "Click Button" for example
|
||||
def call_action(self, action_name, selector=None, optional_value=None):
|
||||
if self.page is None:
|
||||
logger.warning("Cannot call action on None page object")
|
||||
return
|
||||
raise Exception("Could not connect to the virtual browser. Session may have expired.")
|
||||
|
||||
# Check if session has expired
|
||||
if self.has_expired:
|
||||
logger.warning("Attempting to use an expired browser session")
|
||||
raise Exception("Browser session has expired. Please reload the page to start a new session.")
|
||||
|
||||
now = time.time()
|
||||
call_action_name = re.sub('[^0-9a-zA-Z]+', '_', action_name.lower())
|
||||
@@ -97,7 +114,7 @@ class steppable_browser_interface():
|
||||
# Check if action handler exists
|
||||
if not hasattr(self, "action_" + call_action_name):
|
||||
logger.warning(f"Action handler for '{call_action_name}' not found")
|
||||
return
|
||||
raise Exception(f"Unknown browser operation: {action_name}")
|
||||
|
||||
action_handler = getattr(self, "action_" + call_action_name)
|
||||
|
||||
@@ -116,12 +133,23 @@ class steppable_browser_interface():
|
||||
self.safe_page_operation(wait_timeout)
|
||||
logger.debug(f"Call action done in {time.time()-now:.2f}s")
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing action '{call_action_name}': {str(e)}")
|
||||
error_message = str(e)
|
||||
logger.error(f"Error executing action '{call_action_name}': {error_message}")
|
||||
|
||||
# Request garbage collection to free up resources after error
|
||||
try:
|
||||
self.page.request_gc()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Rethrow with improved messages for specific error types
|
||||
if "'Response' object is not subscriptable" in error_message:
|
||||
raise Exception("Could not connect to the virtual browser. Connection was lost or failed.")
|
||||
elif "session closed" in error_message.lower() or "target closed" in error_message.lower():
|
||||
raise Exception("Browser session has closed. Please reload the page to start a new session.")
|
||||
else:
|
||||
# Rethrow the exception to be handled by the caller
|
||||
raise
|
||||
|
||||
def action_goto_url(self, selector=None, value=None):
|
||||
if not value:
|
||||
@@ -483,7 +511,12 @@ class browsersteps_live_ui(steppable_browser_interface):
|
||||
# Safety check - don't proceed if resources are cleaned up
|
||||
if self._is_cleaned_up or self.page is None:
|
||||
logger.warning("Attempted to get current state after cleanup")
|
||||
return (None, None)
|
||||
raise Exception("Could not connect to the virtual browser. Session may have expired.")
|
||||
|
||||
# Check if session has expired
|
||||
if self.has_expired:
|
||||
logger.warning("Attempting to use an expired browser session")
|
||||
raise Exception("Browser session has expired. Please reload the page to start a new session.")
|
||||
|
||||
xpath_element_js = importlib.resources.files("changedetectionio.content_fetchers.res").joinpath('xpath_element_scraper.js').read_text()
|
||||
|
||||
@@ -496,6 +529,9 @@ class browsersteps_live_ui(steppable_browser_interface):
|
||||
try:
|
||||
# Get screenshot first
|
||||
screenshot = capture_full_page(page=self.page)
|
||||
if screenshot is None:
|
||||
raise Exception("Could not capture screenshot from the virtual browser")
|
||||
|
||||
logger.debug(f"Time to get screenshot from browser {time.time() - now:.2f}s")
|
||||
|
||||
# Then get interactive elements
|
||||
@@ -510,6 +546,9 @@ class browsersteps_live_ui(steppable_browser_interface):
|
||||
"visualselector_xpath_selectors": scan_elements,
|
||||
"max_height": MAX_TOTAL_HEIGHT
|
||||
}))
|
||||
if xpath_data is None:
|
||||
raise Exception("Could not extract page elements from the virtual browser")
|
||||
|
||||
self.page.request_gc()
|
||||
|
||||
# Sort elements by size
|
||||
@@ -517,18 +556,33 @@ class browsersteps_live_ui(steppable_browser_interface):
|
||||
logger.debug(f"Time to scrape xPath element data in browser {time.time()-now:.2f}s")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting current state: {str(e)}")
|
||||
error_message = str(e)
|
||||
logger.error(f"Error getting current state: {error_message}")
|
||||
|
||||
# Attempt recovery - force garbage collection
|
||||
try:
|
||||
self.page.request_gc()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Raise appropriate error message
|
||||
if "'Response' object is not subscriptable" in error_message:
|
||||
raise Exception("Could not connect to the virtual browser. Connection was lost or failed.")
|
||||
elif "session closed" in error_message.lower() or "target closed" in error_message.lower():
|
||||
raise Exception("Browser session has closed. Please reload the page to start a new session.")
|
||||
else:
|
||||
# Rethrow the exception to be handled by the caller
|
||||
raise
|
||||
|
||||
# Request garbage collection one final time
|
||||
try:
|
||||
self.page.request_gc()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Final validation before returning
|
||||
if screenshot is None or xpath_data is None:
|
||||
raise Exception("Could not retrieve data from the virtual browser")
|
||||
|
||||
return (screenshot, xpath_data)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user