{"record":{"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/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/client_middleware_digest_auth.py#L185-L221","documentation":"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.","triggerScenarios":"Constructing DigestAuthMiddleware(login=None, password='x'). Also when login is loaded from config/env that returned None and forwarded without a check.","commonSituations":"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.","solutions":["Supply a concrete login string: DigestAuthMiddleware(login='user', password='x').","Validate at the config boundary and fail loudly if either credential is missing.","Read credentials via a secrets helper (e.g. devkey) that never returns None."],"exampleFix":"// before\nmw = DigestAuthMiddleware(login=os.getenv('API_USER'), password=os.getenv('API_PASS'))\n// after\nuser = os.environ['API_USER']  # raises if missing rather than silently None\nmw = DigestAuthMiddleware(login=user, password=os.environ['API_PASS'])","handlingStrategy":"validation","validationCode":"def require_login(login):\n    if login is None:\n        raise ValueError('login is required')\n    return login\n\nDigestAuthMiddleware(login=require_login(login), password=password)","typeGuard":"def is_nonnull_login(v) -> bool:\n    return v is not None and isinstance(v, str)","tryCatchPattern":"try:\n    mw = DigestAuthMiddleware(login=login, password=password)\nexcept ValueError as e:\n    if 'login' in str(e):\n        raise SystemExit('API username not configured')\n    raise","preventionTips":["Load credentials from env with os.environ[...] (KeyError beats silent None).","Validate credential presence in a single config-loading helper.","Use a secrets manager that errors on missing keys."],"tags":["authentication","digest-auth","validation","configuration"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}