affaan-m/ECC · error · TimeoutError
Condition not met within {timeout}s
Error message
Condition not met within {timeout}s What it means
A TimeoutError raised by wait_until() in a Windows desktop UI automation helper when a polled condition fn() never returns truthy within the timeout window. wait_until is the fallback path used when UIA events are unreliable. It polls every `interval` seconds until `timeout` elapses, swallowing per-iteration exceptions.
Source
Thrown at skills/windows-desktop-e2e/SKILL.md:175
return spec
def wait_window(self, title, timeout=ACTION_TIMEOUT):
"""Wait for a new top-level window (dialogs, child windows)."""
dlg = Desktop(backend="uia").window(title=title)
dlg.wait("visible", timeout=timeout)
return dlg
def wait_until(self, fn, timeout=ACTION_TIMEOUT, interval=0.3):
"""Poll an arbitrary condition — use when UIA events are unreliable."""
deadline = time.time() + timeout
while time.time() < deadline:
try:
if fn():
return True
except Exception:
pass
time.sleep(interval)
raise TimeoutError(f"Condition not met within {timeout}s")
# --- Actions ---
def click(self, spec):
self.wait_visible(spec)
spec.click_input()
def type_text(self, spec, text):
self.wait_visible(spec)
ctrl = spec.wrapper_object()
try:
ctrl.set_edit_text(text)
except Exception as e:
# Qt 5.x fallback: UIA Value Pattern may be incomplete
import sys, pywinauto.keyboard as kb
print(f"[windows-desktop-e2e] set_edit_text failed ({e}), using keyboard fallback", file=sys.stderr)
ctrl.click_input()
kb.send_keys("^a")View on GitHub (pinned to 01e15490f0)
Solutions
- Increase timeout only after confirming the control is reachable; first capture a screenshot at failure time to see the real UI state.
- Verify the spec/selector still matches the current build of the app under test.
- Make sure the test session is interactive (not locked) and the app window is foregrounded before polling.
- Replace blind polling with a UIA event listener where possible; wait_until is explicitly the fallback for when events are unreliable.
- Log the last exception swallowed inside the loop so silent failures during polling are diagnosable.
Example fix
# before
while time.time() < deadline:
try:
if fn():
return True
except Exception:
pass
time.sleep(interval)
raise TimeoutError(f"Condition not met within {timeout}s")
# after: capture the last error for diagnosis
last_err = None
while time.time() < deadline:
try:
if fn():
return True
except Exception as e:
last_err = e
time.sleep(interval)
raise TimeoutError(f"Condition not met within {timeout}s; last error: {last_err}") Defensive patterns
Strategy: retry
Validate before calling
def condition_likely_reachable(spec) -> bool:
# precondition: window is foreground and control exists in the tree
try:
return app.top_window().exists() and spec.exists(timeout=1)
except Exception:
return False
if not condition_likely_reachable(spec):
raise RuntimeError("target window/control not present; aborting before wait_until") Type guard
null
Try / catch
try:
helper.wait_until(lambda: spec.exists(), timeout=30)
except TimeoutError as e:
screenshot("artifacts/wait_timeout.png")
raise AssertionError(f"{e}; see artifacts/wait_timeout.png") from e Prevention
- Foreground the app window and confirm the session is interactive before polling.
- Capture a screenshot on timeout for postmortem.
- Prefer UIA event listeners over blind polling.
- Log the last exception swallowed inside the poll loop.
When it happens
Trigger: Calling wait_until(fn, timeout, interval) where fn() keeps returning False, or keeps raising exceptions, for the full timeout. Common when the target control never appears, the wrong window is focused, or the automation selector does not match the rendered UI.
Common situations: UI changed between recordings so the selector no longer resolves; the app is slow to render under CI load; a modal blocked the target; the test machine is locked/screensaver-on so UIA returns nothing; DPI scaling mismatch hid the control.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Image not found on screen: {template_path}
- Template unreadable: {template_path}
- --timeout-ms must be a positive number
- File path contains unsafe shell characters
- Formatter exited with status ${result.status}
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/cf0d53fcb47c8192.
Report an issue: GitHub.