aio-libs/aiohttp · error · ValueError

None is not allowed as password value

Error message

None is not allowed as password value

What it means

Raised in `DigestAuthMiddleware.__init__` (client_middleware_digest_auth.py:205-206) when `password` is None. Like the login check, it precedes the `.encode('utf-8')` at line 213. The password is required material for the digest hash, so None is rejected explicitly rather than crashing on encode.

Source

Thrown at aiohttp/client_middleware_digest_auth.py:206

    - RFC 1945: Section 11.1 (username restrictions)

    Implementation notes:
    The core digest calculation is inspired by the implementation in
    https://github.com/requests/requests/blob/v2.18.4/requests/auth.py
    with added support for modern digest auth features and error handling.
    """

    def __init__(
        self,
        login: str,
        password: str,
        preemptive: bool = True,
    ) -> None:
        if login is None:
            raise ValueError("None is not allowed as login value")

        if password is None:
            raise ValueError("None is not allowed as password value")

        if ":" in login:
            raise ValueError('A ":" is not allowed in username (RFC 1945#section-11.1)')

        self._login_str: Final[str] = login
        self._login_bytes: Final[bytes] = login.encode("utf-8")
        self._password_bytes: Final[bytes] = password.encode("utf-8")

        self._last_nonce_bytes = b""
        self._nonce_count = 0
        self._challenge: DigestAuthChallenge = {}
        self._preemptive: bool = preemptive
        # Set of URLs defining the protection space
        self._protection_space: list[str] = []
        # Origin the credentials are scoped to; set on the first request.
        self._origin: URL | None = None

    async def _encode(self, method: str, url: URL, body: Payload | Literal[b""]) -> str:

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Ensure `password` is a non-None string (empty string `''` is technically allowed since only None is rejected, though a real secret should be non-empty).
  2. Load from env with a guard: `pw = os.environ.get('DIGEST_PASS'); if pw is None: raise ...`.
  3. Use a secret manager that raises on missing keys instead of returning None.

Example fix

// before
mw = DigestAuthMiddleware(login='u', password=os.environ.get('PW'))
// after
pw = os.environ.get('DIGEST_PASS')
if pw is None:
    raise RuntimeError('DIGEST_PASS must be set')
mw = DigestAuthMiddleware(login='u', password=pw)
Defensive patterns

Strategy: validation

Validate before calling

def require_password(pw: str | None) -> str:
    if pw is None:
        raise ValueError('password must not be None')
    return pw

Type guard

def is_valid_password(v) -> bool:
    return isinstance(v, str)

Prevention

When it happens

Trigger: Constructing `DigestAuthMiddleware(login='u', password=None)`; reading a password from a secret store that returned None; using `getpass.getpass()` in a non-interactive context that returned None.

Common situations: Secret manager / vault call returning None on missing key; CI environment without the password env var set; test fixtures that forgot to populate the password.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/6f3d95759efb03dd.json. Report an issue: GitHub.