{"record":{"id":"a2c07a5de8abbdff","repo":"jumpserver/jumpserver","slug":"expression-should-be-a-regular-expression","errorCode":null,"errorMessage":"{expression} should be a regular expression","messagePattern":"(.+?) should be a regular expression","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"apps/libs/ansible/modules_utils/remote_client.py","lineNumber":858,"sourceCode":"        if answers is None:\n            result = []\n        elif isinstance(answers, (str, re.Pattern)):\n            result = [answers]\n        elif isinstance(answers, (list, tuple)):\n            result = list(answers)\n        else:\n            raise ValueError('Answers must be a regular expression or a list')\n\n        if len(result) < len(commands):\n            result.extend([DEFAULT_RE] * (len(commands) - len(result)))\n        return result\n\n    @staticmethod\n    def __match(expression, content):\n        if isinstance(expression, str):\n            expression = re.compile(expression, re.DOTALL | re.IGNORECASE)\n        elif not isinstance(expression, re.Pattern):\n            raise ValueError(f'{expression} should be a regular expression')\n\n        return bool(expression.search(content))\n\n    @staticmethod\n    def _channel_is_closed(channel):\n        return bool(\n            getattr(channel, 'closed', False)\n            or getattr(channel, 'eof_received', False)\n        )\n\n    @raise_timeout('Recv message')\n    def _get_match_recv(\n        self,\n        answer_reg=DEFAULT_RE,\n        allow_quiet=False,\n        quiet_period=None,\n    ):\n        buffer_str = ''","sourceCodeStart":840,"sourceCodeEnd":876,"githubUrl":"https://github.com/jumpserver/jumpserver/blob/6ec464fabd61b95912d539455a3a5f15f5c59fe0/apps/libs/ansible/modules_utils/remote_client.py#L840-L876","documentation":"__match() accepts either a plain string (compiled with DOTALL|IGNORECASE) or a precompiled re.Pattern. Any other type (dict, int, compiled-with-wrong-type, bytes, etc.) raises ValueError.","triggerScenarios":"Internal/_get_match_recv callers passing an expression that is not str or re.Pattern — in practice, user-supplied answer regexes that are, e.g., bytes or a list element of wrong type reaching the matcher.","commonSituations":"Passing bytes regex b'Password:', passing a list as a single answer, or a custom object with __str__ that was never stringified.","solutions":["Ensure every answer/pattern passed to execute()/switch_user APIs is a str or re.Pattern","Decode bytes patterns to str","Flatten nested lists of answers"],"exampleFix":"# before\nclient.execute(['enable'], answers=[b'Password:'])\n\n# after\nclient.execute(['enable'], answers=['Password:'])","handlingStrategy":"validation","validationCode":"assert all(isinstance(a, (str, re.Pattern)) for a in answers), 'answers must be str or re.Pattern'","typeGuard":"def is_pattern_like(x) -> bool:\n    return isinstance(x, str) or isinstance(x, re.Pattern)","tryCatchPattern":null,"preventionTips":["Normalize answers to compiled re.Pattern early in your code","Never pass bytes as patterns"],"tags":["validation","regex","type-error"],"backgroundTag":"invalid-regex-argument","analyzedSha":"6ec464fabd61b95912d539455a3a5f15f5c59fe0","analyzedAt":"2026-08-28T11:33:00.925Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}