You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2026/04/27 01:04:29 UTC
(airflow) branch v3-2-test updated: [v3-2-test] Fix UI log fetch crash when ti.hostname is empty (#64285) (#65583)
This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch v3-2-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-2-test by this push:
new ce562b763c4 [v3-2-test] Fix UI log fetch crash when ti.hostname is empty (#64285) (#65583)
ce562b763c4 is described below
commit ce562b763c42d2ca86484225c584c15f8d268cb8
Author: github-actions[bot] <41...@users.noreply.github.com>
AuthorDate: Mon Apr 27 03:04:20 2026 +0200
[v3-2-test] Fix UI log fetch crash when ti.hostname is empty (#64285) (#65583)
* Fix UI log fetch crash when ti.hostname is empty
When tasks fail to report their hostname (e.g., due to cellular worker proxy overrides dropping the internal API patched payload), the TaskInstance hostname defaults to empty. This causes _get_log_retrieval_url to construct an invalid URL ('http://:8793/...') which completely crashes log serving with an 'Invalid URL... No host supplied' error from python requests.
closes: #64263
* Fix UI log fetch crash when ti.hostname is empty: address reviewer feedback
* Fix mypy static checks and add hostname_callable config help
* Update airflow-core/src/airflow/utils/log/file_task_handler.py
---------
(cherry picked from commit 7c3eacc3cfb9104128def18faed221fd4f4b2cd3)
Co-authored-by: Gourav N S S <go...@gmail.com>
Co-authored-by: Jason(Zhe-You) Liu <68...@users.noreply.github.com>
---
.../src/airflow/utils/log/file_task_handler.py | 13 ++++++++-
.../tests/unit/utils/log/test_file_task_handler.py | 31 ++++++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/airflow-core/src/airflow/utils/log/file_task_handler.py b/airflow-core/src/airflow/utils/log/file_task_handler.py
index 13138d1cff5..1820833aaa1 100644
--- a/airflow-core/src/airflow/utils/log/file_task_handler.py
+++ b/airflow-core/src/airflow/utils/log/file_task_handler.py
@@ -709,7 +709,7 @@ class FileTaskHandler(logging.Handler):
ti: TaskInstance | TaskInstanceHistory,
log_relative_path: str,
log_type: LogType | None = None,
- ) -> tuple[str, str]:
+ ) -> tuple[str | None, str | None]:
"""Given TI, generate URL with which to fetch logs from service log server."""
if log_type == LogType.TRIGGER:
if not ti.triggerer_job:
@@ -722,6 +722,10 @@ class FileTaskHandler(logging.Handler):
hostname = ti.hostname
config_key = "worker_log_server_port"
config_default = 8793
+
+ if not hostname:
+ return None, None
+
return (
urljoin(
f"http://{hostname}:{conf.get('logging', config_key, fallback=config_default)}/log/",
@@ -905,6 +909,13 @@ class FileTaskHandler(logging.Handler):
try:
log_type = LogType.TRIGGER if getattr(ti, "triggerer_job", False) else LogType.WORKER
url, rel_path = self._get_log_retrieval_url(ti, worker_log_rel_path, log_type=log_type)
+ if not url or not rel_path:
+ sources.append(
+ f"Could not read served logs: Hostname not available for "
+ f"{log_type.value}. "
+ f"Please check your `hostname_callable` configuration."
+ )
+ return sources, log_streams
response = _fetch_logs_from_service(url, rel_path)
if response.status_code == 403:
sources.append(
diff --git a/airflow-core/tests/unit/utils/log/test_file_task_handler.py b/airflow-core/tests/unit/utils/log/test_file_task_handler.py
index 93606cffe1d..ceecffafa32 100644
--- a/airflow-core/tests/unit/utils/log/test_file_task_handler.py
+++ b/airflow-core/tests/unit/utils/log/test_file_task_handler.py
@@ -91,6 +91,37 @@ class TestFileTaskHandlerLogServer:
assert "secret_key" in sources[0]
assert streams == []
+ @patch("airflow.utils.log.file_task_handler._fetch_logs_from_service")
+ @patch.object(FileTaskHandler, "_get_log_retrieval_url")
+ def test_read_from_logs_server_no_hostname(self, mock_get_url, mock_fetch):
+ """When hostname is missing, show a clear message instead of attempting log server fetch."""
+ mock_get_url.return_value = (None, None)
+
+ sources, streams = self.handler._read_from_logs_server(self.ti, "dag/run/task/1.log")
+
+ assert len(sources) == 1
+ assert "Hostname not available" in sources[0]
+ assert "worker" in sources[0]
+ assert streams == []
+ mock_fetch.assert_not_called()
+
+ @patch("airflow.utils.log.file_task_handler._fetch_logs_from_service")
+ @patch.object(FileTaskHandler, "_get_log_retrieval_url")
+ def test_read_from_logs_server_no_hostname_triggerer(self, mock_get_url, mock_fetch):
+ """When hostname is missing for triggerer, show a clear message instead of attempting log server fetch."""
+ mock_get_url.return_value = (None, None)
+ self.ti.triggerer_job = MagicMock()
+ self.ti.triggerer_job.hostname = None
+ self.ti.triggerer_job.id = 123
+
+ sources, streams = self.handler._read_from_logs_server(self.ti, "dag/run/task/1.log")
+
+ assert len(sources) == 1
+ assert "Hostname not available" in sources[0]
+ assert "trigger" in sources[0]
+ assert streams == []
+ mock_fetch.assert_not_called()
+
class TestFileTaskHandlerReadFromLocal:
"""Tests for ``FileTaskHandler._read_from_local`` path containment."""