{"id":"2563704ed998e3d8","repo":"redis/redis-py","slug":"subcommand-subcommand-name-not-found-in-command","errorCode":null,"errorMessage":"Subcommand {subcommand_name} not found in command {command_name}","messagePattern":"Subcommand (.+?) not found in command (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"redis/_parsers/commands.py","lineNumber":245,"sourceCode":"                The name of the subcommand to check, if applicable. If not provided,\n                the check is performed only on the command.\n\n        Returns:\n            bool\n                True if the specified command or subcommand is considered keyless,\n                False otherwise.\n\n        Raises:\n            ValueError\n                If the specified subcommand is not found within the command or the\n                specified command does not exist in the available commands.\n        \"\"\"\n        if subcommand_name:\n            for subcommand in self.commands.get(command_name)[\"subcommands\"]:\n                if str_if_bytes(subcommand[0]) == subcommand_name:\n                    parsed_subcmd = self.parse_subcommand(subcommand)\n                    return parsed_subcmd[\"first_key_pos\"] <= 0\n            raise ValueError(\n                f\"Subcommand {subcommand_name} not found in command {command_name}\"\n            )\n        else:\n            command_details = self.commands.get(command_name, None)\n            if command_details is not None:\n                return command_details[\"first_key_pos\"] <= 0\n\n            raise ValueError(f\"Command {command_name} not found in commands\")\n\n    def get_command_policies(self) -> PolicyRecords:\n        \"\"\"\n        Retrieve and process the command policies for all commands and subcommands.\n\n        This method traverses through commands and subcommands, extracting policy details\n        from associated data structures and constructing a dictionary of commands with their\n        associated policies. It supports nested data structures and handles both main commands\n        and their subcommands.\n","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/_parsers/commands.py#L227-L263","documentation":"Raised by CommandsParser._is_keyless_command() when a subcommand_name is supplied (e.g. 'CLUSTER|NODES') but no entry in command['subcommands'] matches that 'parent|child' string. The method walks the COMMAND-supplied subcommand list and raises ValueError naming both the missing subcommand and its parent. This is a programmer/server-metadata error, not a network error.","triggerScenarios":"Cluster client building command policies at startup (get_command_policies) when COMMAND reports a parent command with subcommands but the specific 'parent|child' token the parser built is absent - e.g. a new subcommand the parser does not yet know, or a renamed/custom subcommand. Also triggered by passing a fabricated subcommand name in custom routing code.","commonSituations":"Upgrading Redis to a version that added/renamed subcommands while running an older redis-py; running a Redis fork that emits non-standard subcommand names; manually invoking _is_keyless_command with an unvalidated subcommand.","solutions":["Upgrade redis-py to a release that knows the subcommands your Redis version emits.","Confirm the subcommand actually exists via 'redis-cli COMMAND INFO <parent>'.","If you call _is_keyless_command directly, validate subcommand_name against the command's subcommand list first.","Report the subcommand string in the error to the maintainers so the parser table can be extended."],"exampleFix":"// before\nparser._is_keyless_command(\"cluster\", \"BOGUS\")  # ValueError\n\n// after\nvalid = [str_if_bytes(s[0]) for s in parser.commands[\"cluster\"][\"subcommands\"]]\nif \"cluster|BOGUS\" not in valid:\n    raise KeyError(f\"unknown subcommand; valid: {valid}\")","handlingStrategy":"validation","validationCode":"# Validate subcommand against the parser's known list before calling\ndef assert_subcommand_known(parser, parent, sub):\n    subs = parser.commands.get(parent, {}).get(\"subcommands\") or []\n    names = [s[0].decode() if isinstance(s[0], bytes) else s[0] for s in subs]\n    if f\"{parent}|{sub.upper()}\" not in names:\n        raise KeyError(f\"unknown subcommand; valid: {names}\")","typeGuard":null,"tryCatchPattern":"try:\n    parser._is_keyless_command(\"cluster\", sub)\nexcept ValueError as e:\n    if \"Subcommand\" in str(e):\n        # unknown subcommand; do not retry, fix the name or upgrade client\n        raise","preventionTips":["Upgrade redis-py alongside Redis upgrades so subcommand tables stay in sync.","Validate subcommand names against COMMAND INFO output before custom routing.","Avoid calling private _is_keyless_command directly; use the public cluster API.","Report missing subcommand strings upstream so the parser table grows."],"tags":["cluster","commands","validation","sync"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}