This is an automated email from the ASF dual-hosted git repository.

vatsrahul1001 pushed a commit to branch v3-4-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit e63e6bb5244b4c62b8384ff9990f944ae5a92840
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Nov 4 02:17:17 2026 +0000

    [v3-4-test] Fix retry policy docs and the example Dag's `ConnectionError` 
rules (#76313) (#76406)
    
    The example Dag's ConnectionError rules used the builtin ConnectionError, 
which requests.exceptions.ConnectionError does not subclass, so they never 
matched what the example task raises. The docs marked RESCHEDULE as added in 
3.5.0 (it shipped in 3.4.0), quoted a chain reason the page's own policy cannot 
produce, said every policy decision is logged (only decisions with a reason 
are), and listed only AirflowFailException as skipping the policy.
    (cherry picked from commit 1631eccfc2d72764bd0d3f15b4831ad25e031b51)
    
    Co-authored-by: Kaxil Naik <[email protected]>
---
 airflow-core/docs/core-concepts/tasks.rst          | 33 ++++++++++++++--------
 .../airflow/example_dags/example_retry_policy.py   | 13 +++------
 2 files changed, 26 insertions(+), 20 deletions(-)

diff --git a/airflow-core/docs/core-concepts/tasks.rst 
b/airflow-core/docs/core-concepts/tasks.rst
index f57de76baa8..128ad8c2ba7 100644
--- a/airflow-core/docs/core-concepts/tasks.rst
+++ b/airflow-core/docs/core-concepts/tasks.rst
@@ -215,8 +215,11 @@ How it works
 ~~~~~~~~~~~~
 
 The policy runs in the **task worker process** (never the scheduler) between 
catching the
-exception and deciding the task's next state. Each policy decision is logged 
in the task
-logs as ``Retry policy decision action=<action> reason=<reason>``.
+exception and deciding the task's next state. A decision that has a reason is 
logged in the
+task logs as ``Retry policy decision action=<action> reason=<reason>``. A 
matching
+``RetryRule`` always gives one (its ``reason``, or ``Matched rule for 
<exception type>``), and
+so does ``ChainRetryPolicy``. When no rule matches, or a custom policy returns 
a decision
+without a reason, no line is logged.
 
 When a task fails, the policy evaluates the exception and returns one of four 
actions:
 
@@ -263,15 +266,15 @@ Composition with existing parameters
        A reschedule without its own delay waits ``retry_delay``, without 
backoff.
    * - ``on_retry_callback``
      - Fires on retries only, including policy-driven retries.
-   * - ``AirflowFailException``
-     - Always takes precedence. The policy is never consulted for this 
exception.
+   * - ``AirflowFailException``, ``AirflowSensorTimeout``, 
``AirflowTaskTerminated``
+     - Always take precedence. The task fails without consulting the policy.
 
 .. _concepts:retry-policies-reschedule:
 
 Rescheduling without using a retry
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. versionadded:: 3.5.0
+.. versionadded:: 3.4.0
 
 Some failures mean "not yet" rather than "this went wrong": the system the 
task drives is
 out of capacity, such as a Kubernetes namespace whose resource quota is full 
or a warehouse
@@ -355,11 +358,15 @@ Mapped tasks
 ~~~~~~~~~~~~
 
 Policies work with task mapping. The policy applies per mapped task
-instance -- if instance 2 of 10 hits FAIL, the other 9 continue independently:
+instance -- if instance 2 of 10 hits FAIL, the other 9 continue independently. 
Using
+``STANDARD_RETRY_POLICY`` from the previous example:
 
 .. code-block:: python
 
-    @task(retry_policy=my_policy)
+    from airflow.sdk import task
+
+
+    @task(retry_policy=STANDARD_RETRY_POLICY)
     def my_mapped_task(input): ...
 
 
@@ -369,7 +376,9 @@ For a classic operator, pass the policy to ``.partial()``:
 
 .. code-block:: python
 
-    BashOperator.partial(task_id="run", retry_policy=my_policy).expand(
+    from airflow.providers.standard.operators.bash import BashOperator
+
+    BashOperator.partial(task_id="run", 
retry_policy=STANDARD_RETRY_POLICY).expand(
         bash_command=["echo 1", "echo 2", "echo 3"],
     )
 
@@ -409,9 +418,11 @@ Two consequences of "DEFAULT means next" to keep in mind:
 A policy that raises an ordinary exception, or returns something other than a
 ``RetryDecision``, is logged and treated as DEFAULT, so one broken policy does 
not take the
 rules after it down with it. The winning decision's reason names the policy 
that decided and
-then what the earlier ones said (``HTTPStatusRetryPolicy: HTTP 503 (after 
ExceptionRetryPolicy:
-no decision)``). That string is the task's ``retry_reason`` on a FAIL as well 
as a RETRY; when
-no policy decided, it appears only in the task log as the ``Retry policy 
decision`` line.
+then what the earlier ones said, for example ``HTTPStatusRetryPolicy: HTTP 404 
(after
+ExceptionRetryPolicy: no decision)``. A decision with no reason of its own 
shows its action
+instead, such as ``HTTPStatusRetryPolicy: retry``. That string is the task's 
``retry_reason``
+on a FAIL as well as a RETRY; when no policy decided, it appears only in the 
task log as the
+``Retry policy decision`` line.
 
 Custom retry policies
 ~~~~~~~~~~~~~~~~~~~~~
diff --git a/airflow-core/src/airflow/example_dags/example_retry_policy.py 
b/airflow-core/src/airflow/example_dags/example_retry_policy.py
index 383c5082a68..546c2ca1759 100644
--- a/airflow-core/src/airflow/example_dags/example_retry_policy.py
+++ b/airflow-core/src/airflow/example_dags/example_retry_policy.py
@@ -24,9 +24,9 @@ using a retry, or fall through to the standard retry logic.
 
 from __future__ import annotations
 
+# [START retry_policy_definition]
 from datetime import timedelta
 
-# [START retry_policy_definition]
 from airflow.sdk import DAG, ChainRetryPolicy, ExceptionRetryPolicy, 
RetryAction, RetryRule, task
 
 API_RETRY_POLICY = ExceptionRetryPolicy(
@@ -43,7 +43,7 @@ API_RETRY_POLICY = ExceptionRetryPolicy(
             reason="Auth failure, not retryable",
         ),
         RetryRule(
-            exception=ConnectionError,
+            exception="requests.exceptions.ConnectionError",
             action=RetryAction.RETRY,
             retry_delay=timedelta(seconds=30),
         ),
@@ -52,12 +52,7 @@ API_RETRY_POLICY = ExceptionRetryPolicy(
 # [END retry_policy_definition]
 
 # [START retry_policy_on_task]
-with DAG(
-    dag_id="example_retry_policy",
-    schedule=None,
-    catchup=False,
-    tags=["example", "retry_policy"],
-):
+with DAG(dag_id="example_retry_policy", tags=["example", "retry_policy"]):
 
     @task(retries=5, retry_delay=timedelta(minutes=1), 
retry_policy=API_RETRY_POLICY)
     def call_external_api():
@@ -75,7 +70,7 @@ with DAG(
 STANDARD_RETRY_POLICY = ExceptionRetryPolicy(
     rules=[
         RetryRule(exception="requests.exceptions.HTTPError", 
action=RetryAction.FAIL),
-        RetryRule(exception=ConnectionError, 
retry_delay=timedelta(seconds=10)),
+        RetryRule(exception="requests.exceptions.ConnectionError", 
retry_delay=timedelta(seconds=10)),
     ],
 )
 # [END retry_policy_reusable]

Reply via email to