Under which category would you file this issue?
Airflow Core
Apache Airflow version
3.2.2
What happened and how to reproduce it?
airflow_shared.observability.metrics.statsd_logger.get_statsd_logger() calls the StatsD client as:
statsd = stats_class(host, port, prefix, ipv6)
What happened
We run Airflow in an IPv6-only Kubernetes environment and send Airflow metrics to a StatsD endpoint.
Even with IPv6 StatsD support enabled:
AIRFLOW__METRICS__STATSD_ON=True
AIRFLOW__METRICS__STATSD_HOST=
AIRFLOW__METRICS__STATSD_PORT=8125
AIRFLOW__METRICS__STATSD_PREFIX=airflow
AIRFLOW__METRICS__STATSD_IPV6=True
Airflow components, for example workers, fail to configure the StatsD metrics client and fall back to NoStatsLogger.
Example log:
{
"logger": "airflow._shared.observability.metrics.stats",
"timestamp": "2026-07-03T18:45:26.741537Z",
"level": "ERROR",
"loc": "stats.py:108",
"message": "Could not configure StatsClient: [Errno -5] No address associated with hostname, using NoStatsLogger instead."
}
As a result, metrics from the affected Airflow component are not emitted.
What I think should happen
When AIRFLOW__METRICS__STATSD_IPV6=True / [metrics] statsd_ipv6 = True is set, Airflow should initialize the default statsd.StatsClient with ipv6=True, so the StatsD host is resolved using IPv6.
Metrics should be sent successfully to an IPv6-only StatsD endpoint.
Probable root cause
The probable issue is in:
airflow_shared/observability/metrics/statsd_logger.py
Current code calls the StatsD client positionally:
statsd = stats_class(host, port, prefix, ipv6)
However, the default statsd.StatsClient constructor has this signature:
StatsClient(
host="localhost",
port=8125,
prefix=None,
maxudpsize=512,
ipv6=False,
)
So Airflow's ipv6 value is passed as the fourth positional argument, which means it lands in maxudpsize, not in ipv6.
Effectively:
stats_class(host, port, prefix, True)
becomes:
StatsClient(
host=host,
port=port,
prefix=prefix,
maxudpsize=True,
ipv6=False,
)
Therefore statsd_ipv6=True never actually enables IPv6 sockets for the default StatsD client.
How to reproduce
Use an IPv6-only hostname for statsd_host, for example a Kubernetes Service that only resolves to an AAAA record.
Set:
AIRFLOW__METRICS__STATSD_ON=True
AIRFLOW__METRICS__STATSD_HOST=
AIRFLOW__METRICS__STATSD_PORT=8125
AIRFLOW__METRICS__STATSD_PREFIX=airflow
AIRFLOW__METRICS__STATSD_IPV6=True
Start an Airflow component that initializes metrics, for example a worker.
Expected: StatsD client is initialized with IPv6 enabled.
Actual: Airflow logs:
Could not configure StatsClient: [Errno -5] No address associated with hostname, using NoStatsLogger instead.
and metrics are not emitted.
This can also be demonstrated with a custom test StatsD client that records received constructor arguments: the ipv6 value is passed as the fourth positional argument instead of the ipv6 keyword argument.
Workaround
We are currently working around this by overriding the StatsD client:
- name: AIRFLOW__METRICS__STATSD_CUSTOM_CLIENT_PATH
value: "settings.metrics.ipv6_statsd_client.IPv6StatsClient"
The custom client detects the Airflow positional-argument behavior and forwards ipv6=True to the parent statsd.StatsClient as a keyword argument:
class IPv6StatsClient(statsd.StatsClient):
def init(
self,
host: str = "localhost",
port: int = 8125,
prefix: str | None = None,
maxudpsize: int = 512,
ipv6: bool = False,
) -> None:
# Airflow passes ipv6 as the 4th positional argument,
# which lands in maxudpsize for statsd.StatsClient.
if isinstance(maxudpsize, bool):
ipv6 = maxudpsize
maxudpsize = 512
super().__init__(
host=host,
port=port,
prefix=prefix,
maxudpsize=maxudpsize,
ipv6=True,
)
This workaround confirms that the same StatsD endpoint works once the underlying statsd.StatsClient is initialized with IPv6 enabled.
Suggested fix
Pass arguments to the StatsD client by keyword instead of positionally:
statsd = stats_class(
host=host,
port=port,
prefix=prefix,
ipv6=ipv6,
)
This should make [metrics] statsd_ipv6 = True work as documented for the default statsd.StatsClient.
A regression test could assert that when ipv6=True is passed to get_statsd_logger(), the configured StatsD client receives ipv6=True as the ipv6 argument, not as maxudpsize.
Related context
This appears related to the previous IPv6 StatsD support change:
#42625
That PR added an option to enable IPv6 address resolution for the StatsD host, but the current positional call appears to prevent the option from taking effect with the default statsd.StatsClient.
What you think should happen instead?
No response
Operating System
No response
Deployment
None
Apache Airflow Provider(s)
No response
Versions of Apache Airflow Providers
No response
Official Helm Chart version
1.22.0 (latest released)
Kubernetes Version
No response
Helm Chart configuration
No response
Docker Image customizations
No response
Anything else?
No response
Are you willing to submit PR?
Code of Conduct
Under which category would you file this issue?
Airflow Core
Apache Airflow version
3.2.2
What happened and how to reproduce it?
airflow_shared.observability.metrics.statsd_logger.get_statsd_logger() calls the StatsD client as:
statsd = stats_class(host, port, prefix, ipv6)
What happened
We run Airflow in an IPv6-only Kubernetes environment and send Airflow metrics to a StatsD endpoint.
Even with IPv6 StatsD support enabled:
AIRFLOW__METRICS__STATSD_ON=True
AIRFLOW__METRICS__STATSD_HOST=
AIRFLOW__METRICS__STATSD_PORT=8125
AIRFLOW__METRICS__STATSD_PREFIX=airflow
AIRFLOW__METRICS__STATSD_IPV6=True
Airflow components, for example workers, fail to configure the StatsD metrics client and fall back to NoStatsLogger.
Example log:
{
"logger": "airflow._shared.observability.metrics.stats",
"timestamp": "2026-07-03T18:45:26.741537Z",
"level": "ERROR",
"loc": "stats.py:108",
"message": "Could not configure StatsClient: [Errno -5] No address associated with hostname, using NoStatsLogger instead."
}
As a result, metrics from the affected Airflow component are not emitted.
What I think should happen
When AIRFLOW__METRICS__STATSD_IPV6=True / [metrics] statsd_ipv6 = True is set, Airflow should initialize the default statsd.StatsClient with ipv6=True, so the StatsD host is resolved using IPv6.
Metrics should be sent successfully to an IPv6-only StatsD endpoint.
Probable root cause
The probable issue is in:
airflow_shared/observability/metrics/statsd_logger.py
Current code calls the StatsD client positionally:
statsd = stats_class(host, port, prefix, ipv6)
However, the default statsd.StatsClient constructor has this signature:
StatsClient(
host="localhost",
port=8125,
prefix=None,
maxudpsize=512,
ipv6=False,
)
So Airflow's ipv6 value is passed as the fourth positional argument, which means it lands in maxudpsize, not in ipv6.
Effectively:
stats_class(host, port, prefix, True)
becomes:
StatsClient(
host=host,
port=port,
prefix=prefix,
maxudpsize=True,
ipv6=False,
)
Therefore statsd_ipv6=True never actually enables IPv6 sockets for the default StatsD client.
How to reproduce
Use an IPv6-only hostname for statsd_host, for example a Kubernetes Service that only resolves to an AAAA record.
Set:
AIRFLOW__METRICS__STATSD_ON=True
AIRFLOW__METRICS__STATSD_HOST=
AIRFLOW__METRICS__STATSD_PORT=8125
AIRFLOW__METRICS__STATSD_PREFIX=airflow
AIRFLOW__METRICS__STATSD_IPV6=True
Start an Airflow component that initializes metrics, for example a worker.
Expected: StatsD client is initialized with IPv6 enabled.
Actual: Airflow logs:
Could not configure StatsClient: [Errno -5] No address associated with hostname, using NoStatsLogger instead.
and metrics are not emitted.
This can also be demonstrated with a custom test StatsD client that records received constructor arguments: the ipv6 value is passed as the fourth positional argument instead of the ipv6 keyword argument.
Workaround
We are currently working around this by overriding the StatsD client:
value: "settings.metrics.ipv6_statsd_client.IPv6StatsClient"
The custom client detects the Airflow positional-argument behavior and forwards ipv6=True to the parent statsd.StatsClient as a keyword argument:
class IPv6StatsClient(statsd.StatsClient):
def init(
self,
host: str = "localhost",
port: int = 8125,
prefix: str | None = None,
maxudpsize: int = 512,
ipv6: bool = False,
) -> None:
# Airflow passes ipv6 as the 4th positional argument,
# which lands in maxudpsize for statsd.StatsClient.
if isinstance(maxudpsize, bool):
ipv6 = maxudpsize
maxudpsize = 512
This workaround confirms that the same StatsD endpoint works once the underlying statsd.StatsClient is initialized with IPv6 enabled.
Suggested fix
Pass arguments to the StatsD client by keyword instead of positionally:
statsd = stats_class(
host=host,
port=port,
prefix=prefix,
ipv6=ipv6,
)
This should make [metrics] statsd_ipv6 = True work as documented for the default statsd.StatsClient.
A regression test could assert that when ipv6=True is passed to get_statsd_logger(), the configured StatsD client receives ipv6=True as the ipv6 argument, not as maxudpsize.
Related context
This appears related to the previous IPv6 StatsD support change:
#42625
That PR added an option to enable IPv6 address resolution for the StatsD host, but the current positional call appears to prevent the option from taking effect with the default statsd.StatsClient.
What you think should happen instead?
No response
Operating System
No response
Deployment
None
Apache Airflow Provider(s)
No response
Versions of Apache Airflow Providers
No response
Official Helm Chart version
1.22.0 (latest released)
Kubernetes Version
No response
Helm Chart configuration
No response
Docker Image customizations
No response
Anything else?
No response
Are you willing to submit PR?
Code of Conduct