{"record":{"id":"b1f1c8e571e2476b","repo":"SeleniumHQ/selenium","slug":"log-level-must-be-one-of-join-levels","errorCode":null,"errorMessage":"log_level must be one of: {', '.join(levels)}","messagePattern":"log_level must be one of: (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"py/selenium/webdriver/remote/server.py","lineNumber":136,"sourceCode":"    def version(self):\n        return self._version\n\n    @version.setter\n    def version(self, version):\n        if version:\n            if not re.match(r\"^\\d+\\.\\d+\\.\\d+$\", str(version)):\n                raise TypeError(f\"{__class__.__name__}.__init__() got an invalid version: '{version}'\")\n        self._version = version\n\n    @property\n    def log_level(self):\n        return self._log_level\n\n    @log_level.setter\n    def log_level(self, log_level):\n        levels = (\"SEVERE\", \"WARNING\", \"INFO\", \"CONFIG\", \"FINE\", \"FINER\", \"FINEST\")\n        if log_level not in levels:\n            raise TypeError(f\"log_level must be one of: {', '.join(levels)}\")\n        self._log_level = log_level\n\n    @property\n    def env(self):\n        return self._env\n\n    @env.setter\n    def env(self, env):\n        if env is not None and not isinstance(env, collections.abc.Mapping):\n            raise TypeError(\"env must be a mapping of environment variables\")\n        self._env = env\n\n    @property\n    def java_path(self):\n        return self._java_path\n\n    @java_path.setter\n    def java_path(self, java_path):","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/SeleniumHQ/selenium/blob/aa36b38e696a0909e973bdf5e2f9031ffe842c4b/py/selenium/webdriver/remote/server.py#L118-L154","documentation":"Raised by the `log_level` property setter on a Selenium server runner when the assigned value is not one of the seven recognized Java logging levels (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST). The library guards the value before storing it so the spawned `java -jar` process receives a legal `--log-level` argument. It surfaces as a TypeError because the bad input is a programmer error, not a runtime condition.","triggerScenarios":"Assigning `server.log_level = 'DEBUG'` (or any casing/typo like 'info ' with trailing space, 'Warn', 'verbose') triggers it. The setter compares membership against the exact tuple, so only the exact canonical strings pass.","commonSituations":"Copy-pasting a level name from another logging framework (Python's logging.DEBUG, log4j DEBUG/TRACE) or from documentation that uses lowercase. Passing an integer level code instead of the string name.","solutions":["Use one of the exact canonical strings: 'SEVERE', 'WARNING', 'INFO', 'CONFIG', 'FINE', 'FINER', 'FINEST'.","If sourcing the level from config, validate it against the allowed set before assigning.","Map your own level vocabulary to these strings rather than passing foreign level names."],"exampleFix":"# before\nserver.log_level = 'debug'\n\n# after\nserver.log_level = 'FINE'","handlingStrategy":"validation","validationCode":"LEVELS = ('SEVERE','WARNING','INFO','CONFIG','FINE','FINER','FINEST')\nif level not in LEVELS:\n    raise ValueError(f'bad log level {level!r}; choose from {LEVELS}')\nserver.log_level = level","typeGuard":"def is_valid_log_level(v) -> bool:\n    return isinstance(v, str) and v in ('SEVERE','WARNING','INFO','CONFIG','FINE','FINER','FINEST')","tryCatchPattern":null,"preventionTips":["Treat the level as an enum, not free text; centralize the allowed set.","Add a unit test that asserts the configured level is in the canonical tuple."],"tags":["configuration","validation","logging","server-runner"],"backgroundTag":null,"analyzedSha":"aa36b38e696a0909e973bdf5e2f9031ffe842c4b","analyzedAt":"2026-08-14T02:32:32.244Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}