{"id":"f5d0d6f70264847f","repo":"aio-libs/aiohttp","slug":"none-is-not-allowed-as-login-value","errorCode":null,"errorMessage":"None is not allowed as login value","messagePattern":"None is not allowed as login value","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/client_middleware_digest_auth.py","lineNumber":203,"sourceCode":"    Standards compliance:\n    - RFC 7616: HTTP Digest Access Authentication (primary reference)\n    - RFC 2617: HTTP Authentication (deprecated by RFC 7616)\n    - RFC 1945: Section 11.1 (username restrictions)\n\n    Implementation notes:\n    The core digest calculation is inspired by the implementation in\n    https://github.com/requests/requests/blob/v2.18.4/requests/auth.py\n    with added support for modern digest auth features and error handling.\n    \"\"\"\n\n    def __init__(\n        self,\n        login: str,\n        password: str,\n        preemptive: bool = True,\n    ) -> None:\n        if login is None:\n            raise ValueError(\"None is not allowed as login value\")\n\n        if password is None:\n            raise ValueError(\"None is not allowed as password value\")\n\n        if \":\" in login:\n            raise ValueError('A \":\" is not allowed in username (RFC 1945#section-11.1)')\n\n        self._login_str: Final[str] = login\n        self._login_bytes: Final[bytes] = login.encode(\"utf-8\")\n        self._password_bytes: Final[bytes] = password.encode(\"utf-8\")\n\n        self._last_nonce_bytes = b\"\"\n        self._nonce_count = 0\n        self._challenge: DigestAuthChallenge = {}\n        self._preemptive: bool = preemptive\n        # Set of URLs defining the protection space\n        self._protection_space: list[str] = []\n        # Origin the credentials are scoped to; set on the first request.","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/client_middleware_digest_auth.py#L185-L221","documentation":"Raised in `DigestAuthMiddleware.__init__` (client_middleware_digest_auth.py:202-203) when `login` is None. The middleware encodes the login to bytes immediately (line 212) and uses it in every digest computation, so a None login cannot proceed. The check is an explicit fail-fast before any encoding happens.","triggerScenarios":"Constructing `DigestAuthMiddleware(login=None, password='x')`; reading credentials from an env var or config that returned None; passing `login=None` as a placeholder.","commonSituations":"Env/config-driven credential loading where the login key is missing (returns None); test code stubbing credentials as None; conditional auth code that constructs the middleware even when credentials aren't set.","solutions":["Ensure `login` is a non-None string before constructing the middleware.","Load credentials with a required-default: `login = os.environ['DIGEST_USER']` (raises KeyError early) or `os.environ.get('DIGEST_USER', '')`.","Conditionally apply the middleware only when credentials are present."],"exampleFix":"// before\nmw = DigestAuthMiddleware(login=os.environ.get('USER'), password=pw)\n// after\nuser = os.environ.get('DIGEST_USER')\nif user is None:\n    raise RuntimeError('DIGEST_USER must be set')\nmw = DigestAuthMiddleware(login=user, password=pw)","handlingStrategy":"validation","validationCode":"def require_login(login: str | None) -> str:\n    if login is None:\n        raise ValueError('login must not be None')\n    return login","typeGuard":"def is_valid_login(v) -> bool:\n    return isinstance(v, str)","tryCatchPattern":null,"preventionTips":["Resolve credentials before constructing the middleware.","Use `os.environ['VAR']` (raises KeyError) instead of `.get()` for required secrets.","Add a startup check that fails loudly if credentials are missing."],"tags":["client","digest-auth","authentication","validation"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}