{"record":{"id":"287aebc1acde6bbc","repo":"microsoft/qlib","slug":"expected-tuple-type-but-got-a-value-key","errorCode":null,"errorMessage":"Expected `tuple` type, but got a value `{key}`","messagePattern":"Expected `tuple` type, but got a value `(.+?)`","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"qlib/model/ens/group.py","lineNumber":111,"sourceCode":"    \"\"\"Group the rolling dict\"\"\"\n\n    def group(self, rolling_dict: dict) -> dict:\n        \"\"\"Given an rolling dict likes {(A,B,R): things}, return the grouped dict likes {(A,B): {R:things}}\n\n        NOTE: There is an assumption which is the rolling key is at the end of the key tuple, because the rolling results always need to be ensemble firstly.\n\n        Args:\n            rolling_dict (dict): an rolling dict. If the key is not a tuple, then do nothing.\n\n        Returns:\n            dict: grouped dict\n        \"\"\"\n        grouped_dict = {}\n        for key, values in rolling_dict.items():\n            if isinstance(key, tuple):\n                grouped_dict.setdefault(key[:-1], {})[key[-1]] = values\n            else:\n                raise TypeError(f\"Expected `tuple` type, but got a value `{key}`\")\n        return grouped_dict\n\n    def __init__(self, ens=RollingEnsemble()):\n        super().__init__(ens=ens)\n","sourceCodeStart":93,"sourceCodeEnd":116,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/model/ens/group.py#L93-L116","documentation":"RollingGroup (qlib/model/ens/group.py:111) groups the output of a rolling ensemble, whose keys are expected to be tuples like (model_key, ..., rolling_tag): it strips the last element as the rolling key. If a key in the passed dict is not a tuple (contrary to the documented contract 'If the key is not a tuple, then do nothing' — the code actually raises), it raises TypeError with the offending key.","triggerScenarios":"Calling RollingGroup.group(rolling_dict) with a dict whose keys are plain strings or other non-tuple values instead of tuples ending with the rolling tag; feeding a model dict produced outside the RollingEnsemble workflow (e.g. {model_key: model} instead of {(model_key, rolling): model}).","commonSituations":"Manually assembling ensemble inputs for rolling models; mixing ensemble outputs from RollingEnsemble (tuple keys) with plain Ensemble (string keys); refactors that change dict key structure.","solutions":["Build the dict with tuple keys whose last element is the rolling tag, e.g. {(model_key, rolling_tag): model}","Use RollingEnsemble's own output as input to RollingGroup.group to guarantee key shape","If you actually have plain string keys, use the plain Ensemble/Group classes instead of RollingGroup","Inspect dict keys before grouping and reject/convert non-tuple keys explicitly"],"exampleFix":"# before\nrolling_dict = {'lgb_model': m1, 'xgb_model': m2}\nRollingGroup().group(rolling_dict)  # TypeError\n\n# after\nrolling_dict = {('lgb_model', 'roll_1'): m1, ('xgb_model', 'roll_1'): m2}\nRollingGroup().group(rolling_dict)","handlingStrategy":"type-guard","validationCode":"bad = [k for k in rolling_dict if not isinstance(k, tuple)]\nassert not bad, f'non-tuple keys not allowed for RollingGroup: {bad}'","typeGuard":"def is_rolling_dict(d: dict) -> bool:\n    return all(isinstance(k, tuple) and len(k) >= 2 for k in d)","tryCatchPattern":"try:\n    grouped = RollingGroup().group(rolling_dict)\nexcept TypeError as e:\n    raise ValueError('use tuple keys (model_key, ..., rolling_tag)') from e","preventionTips":["Always consume RollingEnsemble output as RollingGroup input to preserve tuple keys","Document the expected key shape at ensemble construction sites"],"tags":["qlib","ensemble","rolling","type-error","grouping"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}