Skip to content

RetryConfig(max_attempts=0) runs 5 attempts instead of disabling retries #6293

Description

@Osamaali313

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions