When all LLM communication retries are exhausted, call_with_retry() raises
ExecutionError(kind=\"timeout\"). Two broad except Exception handlers in
Node._execute_agent() and Node.execute() systematically caught this,
converting the timeout into an error string in graph state or — in the
streaming path — an empty response.
Added targeted except ExecutionError as e handlers that check
e.kind and re-raise. Non-timeout ExecutionError instances
(tool errors, etc.) continue to be caught and converted to error strings
for graceful graph continuation.
Extend _is_http_comms_error() in retry.py to recognize provider SDK
connection errors (APIConnectionError, APITimeoutError) by class name,
so they are retried with exponential backoff instead of propagating
immediately.
Add safety net in process_message() to wrap non-retried connection
errors as ExecutionError(kind='timeout') for correct propagation
through the node layer (issue #71).
Two new BDD scenarios verify:
- ExecutionError(kind=\"timeout\") propagates with correct kind/reason
- Non-timeout ExecutionError is still caught gracefully
ISSUES CLOSED: #71