aio-libs/aiohttp · error · ValueError

None is not allowed as login value

Error message

None is not allowed as login value

What it means

Raised by DigestAuthMiddleware.__init__ when login is None. Digest auth requires a non-null username; downstream code calls .encode('utf-8') on login and would crash later with AttributeError, so the constructor fails fast instead.

Solutions

  1. Supply a concrete login string: DigestAuthMiddleware(login='user', password='x').
  2. Validate at the config boundary and fail loudly if either credential is missing.
  3. Read credentials via a secrets helper (e.g. devkey) that never returns None.

Example fix

// before
mw = DigestAuthMiddleware(login=os.getenv('API_USER'), password=os.getenv('API_PASS'))
// after
user = os.environ['API_USER']  # raises if missing rather than silently None
mw = DigestAuthMiddleware(login=user, password=os.environ['API_PASS'])
Defensive patterns

Strategy: validation

Validate before calling

def require_login(login):
    if login is None:
        raise ValueError('login is required')
    return login

DigestAuthMiddleware(login=require_login(login), password=password)

Type guard

def is_nonnull_login(v) -> bool:
    return v is not None and isinstance(v, str)

Try / catch

try:
    mw = DigestAuthMiddleware(login=login, password=password)
except ValueError as e:
    if 'login' in str(e):
        raise SystemExit('API username not configured')
    raise

Prevention

When it happens

Trigger: Constructing DigestAuthMiddleware(login=None, password='x'). Also when login is loaded from config/env that returned None and forwarded without a check.

Common situations: Env var not set (os.getenv returns None). Optional CLI arg defaulting to None. Conditional credential config where one side is populated and the other is not.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/f5d0d6f70264847f. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/client_middleware_digest_auth.py:203

    Standards compliance:
    - RFC 7616: HTTP Digest Access Authentication (primary reference)
    - RFC 2617: HTTP Authentication (deprecated by RFC 7616)
    - 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.

View on GitHub (pinned to d041d4d0fd)