RuntimeError: The object does not exist
Hello, I need help with a persistent issue in TestComplete using Python scripting.
I created a helper method with chatGPT that waits for UI objects and executes actions safely. However, I am constantly getting RuntimeError: The object does not exist, even when the UI object is on screen.
The important detail is that this error happens BEFORE my helper executes, meaning TestComplete tries to resolve the alias too early, even if I pass it inside a lambda.
The errors I am getting include:
RuntimeError: The object does not exist
I tried several approaches: using lambda wrappers, string-based alias evaluation with eval, safe exists checks, try/except wrapping, WaitProperty with catch, RefreshMappingInfo, and returning stub objects. Still, TestComplete tries to resolve the alias too early and throws a RuntimeError before my code handles it.
I want to know if TestComplete officially supports passing object references using lambda in Python without resolving them immediately, or if there is a recommended approach for safe deferred resolution of Alias-based UI objects.
Here is the simplified version of my helper (the stable version):
# ============================================================
# LIB_IfObject.py
# Helper for safe object waits and actions in TestComplete
# ============================================================
class IfObjectHelper:
"""
Waits, validates, and executes actions on TestComplete UI objects,
handling object recreation, timing issues, and temporary unavailability.
"""
@staticmethod
def Run(obj, accion=None, timeout=40000, descripcion="object",
intentos_accion=1, opcional=False):
"""
Waits until the object exists and optionally executes an action.
Parameters:
obj: object reference or lambda returning the object dynamically.
accion: function/lambda to execute over the object.
timeout: maximum wait time in milliseconds.
descripcion: text description for logs.
intentos_accion: number of retries if the action fails.
opcional: if True, missing objects do not fail the test (for optional popups).
"""
try:
timeout = timeout or 40000
start = aqDateTime.Now()
found = False
resolved_obj = None
# === Attempt to resolve the object ===
for _ in range(3):
try:
resolved_obj = obj() if callable(obj) else obj
if not hasattr(resolved_obj, "WaitProperty"):
Delay(100)
continue
resolved_obj.RefreshMappingInfo()
# === Handle RuntimeError for dynamic UI objects ===
try:
# Retry if the object is not instantiated or was recreated
if not getattr(resolved_obj, "Exists", False):
Delay(200)
resolved_obj = obj() if callable(obj) else obj
resolved_obj.RefreshMappingInfo()
except RuntimeError:
# If the handle does not exist yet, wait and retry
Delay(300)
try:
resolved_obj = obj() if callable(obj) else obj
resolved_obj.RefreshMappingInfo()
except:
Delay(100)
# === Extended verification of visibility and enabled state ===
if (resolved_obj.WaitProperty("Exists", True, timeout) and
resolved_obj.WaitProperty("VisibleOnScreen", True, int(timeout / 2))):
# If the object exists but is disabled, treat as informational
if not resolved_obj.Enabled:
Log.Message(f"ℹ {descripcion} found but disabled (action skipped).")
return True
found = True
break
except Exception:
Delay(100)
# === Handle non-existing object ===
if not found:
if opcional or "popup" in descripcion.lower():
Log.Message(f"ℹ {descripcion} not found (optional, skipping).")
return True
else:
Log.Warning(f"❌ {descripcion} not found after {timeout/1000:.1f}s.")
return False
# === Execute action (if provided) ===
if accion:
success = False
for attempt in range(1, intentos_accion + 1):
try:
# Validate that the object still exists
if not getattr(resolved_obj, "Exists", False):
Log.Warning(f"⚠️ {descripcion}: object disappeared before action, retrying...")
try:
resolved_obj = obj() if callable(obj) else obj
resolved_obj.RefreshMappingInfo()
except:
Delay(200)
continue
# Handle actions passed as list/tuple
if isinstance(accion, (list, tuple)):
for sub in accion:
try:
sub()
except Exception as sub_e:
Log.Warning(f"⚠ Sub-action error for {descripcion}: "
f"{type(sub_e).__name__} - {str(sub_e)}")
Delay(100)
else:
# Standard single action
accion()
success = True
Log.Checkpoint(f"✅ {descripcion} found and action executed successfully.")
break
except Exception as e:
# Diagnostic block to identify failing object/action
try:
origin = getattr(resolved_obj, "FullName", str(resolved_obj))
Log.Warning(f"⚠ Attempt {attempt}/{intentos_accion} failed in {descripcion}: "
f"{type(e).__name__} - {str(e)} | Object: {origin}")
except:
Log.Warning(f"⚠ Attempt {attempt}/{intentos_accion} failed in {descripcion}: {str(e)}")
Delay(500)
try:
resolved_obj.RefreshMappingInfo()
except:
Delay(100)
if not success:
Log.Error(f"❌ Action failed in {descripcion} after {intentos_accion} attempts.")
return False
else:
Log.Message(f"✔ {descripcion} found (no action executed).")
# === Total execution time ===
duration = aqDateTime.TimeInterval(start, aqDateTime.Now())
Log.Message(f"⏱ Total time for {descripcion}: {duration:.2f} sec.")
return True
except Exception as e:
import traceback
detail = traceback.format_exc()
Log.Error(f"General error in {descripcion}: {type(e).__name__} - {str(e)}", detail)
return FalseMy questions:
1. Is there an official recommended pattern for safely resolving dynamic alias-based objects in Python for desktop testing?
2. Does TestComplete support passing object references via lambda without resolving them prematurely?
3. Is there any documented workaround for avoiding early alias evaluation inside Python?
Any help will be appreciated. Thank you.