RedactUrlQueryParamsFilter is attached to the global urllib3.connectionpool logger at import (databricks/sql/__init__.py:51). Its filter() iterates record.args with no None guard:
else:
record.args = tuple(
(self.redact(arg) if isinstance(arg, str) else arg)
for arg in record.args # TypeError when record.args is None
)
Normal logging never sets record.args = None (an args-less call yields ()), so this looks safe in isolation. But MLflow attaches its own credential-redaction filter to the same logger (SensitiveQueryParamFilter), and when it redacts a URL it explicitly nulls the args:
if redacted != message:
record.msg = redacted
record.args = None
When both libraries are imported (MLflow first, so its filter runs first), any urllib3.connectionpool record carrying a credentialed URL, e.g. logged on a connection retry, is redacted by MLflow, which sets record.args = None, and then RedactUrlQueryParamsFilter crashes on it.
Minimal repro
import logging, mlflow, databricks.sql
logging.getLogger("urllib3.connectionpool").warning(
"GET %s", "https://bucket.s3/obj?X-Amz-Signature=deadbeef"
)
# TypeError: 'NoneType' object is not iterable
Real-world trigger: MLflow search_traces(..., include_spans=True) against a Databricks tracking store, on a retry of a presigned/credentialed URL during span loading.
Two bugs in the filter:
for arg in record.args has no None guard
- Line 39,
record.arg[k] should be record.args[k]: an AttributeError in the dict-args branch.
Both of these would have been caught with a basic type annotation for the record argument.
Version
databricks-sql-connector==4.4.0; unchanged on main.
RedactUrlQueryParamsFilteris attached to the globalurllib3.connectionpoollogger at import (databricks/sql/__init__.py:51). Itsfilter()iteratesrecord.argswith noNoneguard:Normal logging never sets
record.args = None(an args-less call yields()), so this looks safe in isolation. But MLflow attaches its own credential-redaction filter to the same logger (SensitiveQueryParamFilter), and when it redacts a URL it explicitly nulls the args:When both libraries are imported (MLflow first, so its filter runs first), any
urllib3.connectionpoolrecord carrying a credentialed URL, e.g. logged on a connection retry, is redacted by MLflow, which setsrecord.args = None, and thenRedactUrlQueryParamsFiltercrashes on it.Minimal repro
Real-world trigger: MLflow
search_traces(..., include_spans=True)against a Databricks tracking store, on a retry of a presigned/credentialed URL during span loading.Two bugs in the filter:
for arg in record.argshas noNoneguardrecord.arg[k]should berecord.args[k]: anAttributeErrorin the dict-args branch.Both of these would have been caught with a basic type annotation for the
recordargument.Version
databricks-sql-connector==4.4.0; unchanged onmain.