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

kaxil pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 7f0d50cffc6 Add task log message when the Execution API is unreachable 
(#70290)
7f0d50cffc6 is described below

commit 7f0d50cffc61a48650590078f6832b349860322b
Author: Karen Hsieh <[email protected]>
AuthorDate: Mon Oct 12 16:14:20 2026 -0400

    Add task log message when the Execution API is unreachable (#70290)
    
    * Add task log message when the Execution API is unreachable
    
    * Remove inline comments and spec the mocks in the unreachable API test
    
    Drop the inline comments in the httpx.RequestError handler, as asked in
    review, and give the test's coordinator, logger and log file mocks a
    spec or autospec.
    
    * Assert the unreachable API hint on the real task log file
    
    Write the task log to a temporary folder and read it back, as the
    launch-failure tests do, instead of mocking _configure_logging. The
    test now also checks that the group is closed before the error.
    
    ---------
    
    Co-authored-by: Kaxil Naik <[email protected]>
---
 .../src/airflow/sdk/execution_time/supervisor.py   | 11 +++++++
 .../task_sdk/execution_time/test_supervisor.py     | 36 ++++++++++++++++++++++
 2 files changed, 47 insertions(+)

diff --git a/task-sdk/src/airflow/sdk/execution_time/supervisor.py 
b/task-sdk/src/airflow/sdk/execution_time/supervisor.py
index a5f164daa6d..9677c1e0607 100644
--- a/task-sdk/src/airflow/sdk/execution_time/supervisor.py
+++ b/task-sdk/src/airflow/sdk/execution_time/supervisor.py
@@ -3152,6 +3152,17 @@ def supervise_task(
                 logger=logger or 
structlog.get_logger(logger_name="task").bind(),
                 error=e,
             )
+        except httpx.RequestError as e:
+            if logger is not None:
+                logger.info("::endgroup::")
+                logger.error(
+                    "Could not reach the Execution API server. Verify the 
`[core] "
+                    "execution_api_server_url` (or `[api] base_url`) setting 
points at a reachable "
+                    "Airflow API server.",
+                    server=server,
+                    error=str(e),
+                )
+            raise
         finally:
             if log_path and log_file_descriptor:
                 log_file_descriptor.close()
diff --git a/task-sdk/tests/task_sdk/execution_time/test_supervisor.py 
b/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
index 0f507a87f8e..14e285e3cb3 100644
--- a/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
+++ b/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
@@ -274,6 +274,42 @@ class TestSupervisor:
             with expectation:
                 supervise_task(**kw)
 
+    def test_supervise_task_logs_config_hint_on_connection_error(self, mocker, 
tmp_path):
+        """Test that a connection failure reaching the Execution API is 
surfaced in the task
+        log, naming the responsible config, and still propagates."""
+        ti = TaskInstance(
+            id=uuid7(),
+            task_id="b",
+            dag_id="c",
+            run_id="d",
+            try_number=1,
+            dag_version_id=uuid7(),
+            queue="default",
+        )
+        server = "http://nonexistent-api-server:8080/execution/";
+        coordinator = mocker.Mock(spec=BaseCoordinator)
+        coordinator.execute_task.side_effect = httpx.ConnectError("Name or 
service not known")
+        mocker.patch(
+            "airflow.sdk.execution_time.supervisor.get_coordinator_manager", 
autospec=True
+        ).return_value.for_queue.return_value = coordinator
+
+        with conf_vars({("logging", "base_log_folder"): str(tmp_path)}), 
pytest.raises(httpx.ConnectError):
+            supervise_task(
+                ti=ti,
+                dag_rel_path="c.py",
+                token="",
+                server=server,
+                client=mocker.Mock(spec=sdk_client.Client),
+                bundle_info=BundleInfo(name="my-bundle", version=None),
+                log_path="attempt=1.log",
+            )
+
+        entries = [json.loads(line) for line in (tmp_path / 
"attempt=1.log").read_text().splitlines()]
+        assert [(e["level"], e["event"]) for e in entries] == [("info", 
"::endgroup::"), ("error", mock.ANY)]
+        assert "execution_api_server_url" in entries[1]["event"]
+        assert "base_url" in entries[1]["event"]
+        assert entries[1]["server"] == server
+
 
 def _response_error(status: int, detail: dict[str, Any]) -> 
ServerResponseError:
     error = ServerResponseError.from_response(

Reply via email to