Description
RetryConfig.max_attempts documents that "If 0 or 1, it means no retries. If not specified, default to 5." and defaults to None. But _should_retry_node resolves the default with a falsy-coalescing or:
# src/google/adk/workflow/utils/_retry_utils.py:35
max_attempts = retry_config.max_attempts or 5
0 is falsy, so an explicit RetryConfig(max_attempts=0) (documented as "no retries") is silently replaced with 5, giving 4 retries instead of none. max_attempts=1 works (1 or 5 == 1); only the documented 0 case is broken. The intended "unset" sentinel is None, which must still map to 5.
Reproduction
from google.adk.workflow._retry_config import RetryConfig
from google.adk.workflow._node_state import NodeState
from google.adk.workflow.utils._retry_utils import _should_retry_node
cfg = RetryConfig(max_attempts=0) # documented: no retries
# node on its first (original) attempt:
_should_retry_node(RuntimeError(), cfg, NodeState(attempt_count=1))
# -> True (bug; a workflow node then runs up to 5 attempts total)
Expected
max_attempts=0 (and 1) → no retries; None → default of 5.
Fix
max_attempts = (
retry_config.max_attempts if retry_config.max_attempts is not None else 5
)
Workflow/RetryConfig are public (both in __all__) and this drives every workflow node's retry loop (_NodeRunner._attempt_retry). Happy to send a small PR.
Description
RetryConfig.max_attemptsdocuments that "If 0 or 1, it means no retries. If not specified, default to 5." and defaults toNone. But_should_retry_noderesolves the default with a falsy-coalescingor:0is falsy, so an explicitRetryConfig(max_attempts=0)(documented as "no retries") is silently replaced with5, giving 4 retries instead of none.max_attempts=1works (1 or 5 == 1); only the documented0case is broken. The intended "unset" sentinel isNone, which must still map to 5.Reproduction
Expected
max_attempts=0(and1) → no retries;None→ default of 5.Fix
Workflow/RetryConfigare public (both in__all__) and this drives every workflow node's retry loop (_NodeRunner._attempt_retry). Happy to send a small PR.