{"record":{"id":"7fd4ae33113e4f8f","repo":"Comfy-Org/ComfyUI","slug":"cannot-convert-type-data-to-rangeinput","errorCode":null,"errorMessage":"Cannot convert {type(data)} to RangeInput","messagePattern":"Cannot convert (.+?) to RangeInput","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"comfy_api/latest/_input/range_types.py","lineNumber":40,"sourceCode":"    So midpoint=0.5 → gamma=1.0 (linear).\n    \"\"\"\n\n    def __init__(self, min_val: float, max_val: float, midpoint: float | None = None):\n        self.min_val = min_val\n        self.max_val = max_val\n        self.midpoint = midpoint\n\n    @staticmethod\n    def from_raw(data) -> RangeInput:\n        if isinstance(data, RangeInput):\n            return data\n        if isinstance(data, dict):\n            return RangeInput(\n                min_val=float(data.get(\"min\", 0.0)),\n                max_val=float(data.get(\"max\", 1.0)),\n                midpoint=float(data[\"midpoint\"]) if data.get(\"midpoint\") is not None else None,\n            )\n        raise TypeError(f\"Cannot convert {type(data)} to RangeInput\")\n\n    def to_lut(self, size: int = 256) -> np.ndarray:\n        \"\"\"Generate a float64 lookup table mapping [0, 1] input through this\n        levels adjustment.\n\n        The LUT maps normalized input values (0..1) to output values (0..1),\n        matching the GIMP levels formula.\n        \"\"\"\n        xs = np.linspace(0.0, 1.0, size, dtype=np.float64)\n\n        in_range = self.max_val - self.min_val\n        if abs(in_range) < 1e-10:\n            return np.where(xs >= self.min_val, 1.0, 0.0).astype(np.float64)\n\n        # Normalize: map [min, max] → [0, 1]\n        result = (xs - self.min_val) / in_range\n        result = np.clip(result, 0.0, 1.0)\n","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_api/latest/_input/range_types.py#L22-L58","documentation":"RangeInput.from_raw is the normalization constructor for the levels-adjustment range type used by the new input API: it accepts an already-built RangeInput or a dict with min/max/midpoint keys. Any other Python type (str, list, tuple, number) cannot be interpreted and raises TypeError with the offending type.","triggerScenarios":"Calling RangeInput.from_raw(data) where data is neither RangeInput nor dict — e.g. a JSON string that was never json.loads'd, a list [min, max], or a bare float.","commonSituations":"Frontend sends the range as a JSON-encoded string in request payload; deserialization code assumes list format; passing raw request body fields straight through without schema validation.","solutions":["Pass a dict: RangeInput.from_raw({'min': 0.0, 'max': 1.0, 'midpoint': 0.5})","If the data may be a JSON string, json.loads it before from_raw","Validate the payload shape at the API boundary before conversion"],"exampleFix":"// before\nr = RangeInput.from_raw('{\"min\": 0, \"max\": 1}')  # str -> TypeError\n\n# after\nimport json\nr = RangeInput.from_raw(json.loads(data) if isinstance(data, str) else data)","handlingStrategy":"type-guard","validationCode":"import json\nif isinstance(data, (str, bytes)):\n    data = json.loads(data)\nif not isinstance(data, (dict, RangeInput)):\n    raise TypeError(f'expected dict or RangeInput, got {type(data)}')","typeGuard":"def is_range_raw(d) -> bool:\n    return isinstance(d, (dict, RangeInput))","tryCatchPattern":null,"preventionTips":["Parse JSON strings at the API boundary before type conversion","Validate payload shapes against the expected schema before calling from_raw"],"tags":["input-validation","range","type-conversion","api"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}