{"record":{"id":"48d1b20adfe47c5b","repo":"sgl-project/sglang","slug":"field-name-must-be-a-json-object","errorCode":null,"errorMessage":"{field_name} must be a JSON object","messagePattern":"(.+?) must be a JSON object","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"python/sglang/multimodal_gen/runtime/entrypoints/action/api.py","lineNumber":98,"sourceCode":"    if not isinstance(value, str):\n        return value\n    if not value.strip():\n        return None\n    try:\n        return json.loads(value)\n    except Exception:\n        return value\n\n\ndef _parse_extra_params(value: Any, field_name: str) -> dict[str, Any]:\n    if value in (None, \"\"):\n        return {}\n    try:\n        parsed = json.loads(value) if isinstance(value, str) else value\n    except (json.JSONDecodeError, TypeError) as exc:\n        raise ValueError(f\"{field_name} is not valid JSON\") from exc\n    if not isinstance(parsed, dict):\n        raise ValueError(f\"{field_name} must be a JSON object\")\n    return flatten_extra_params(dict(parsed))\n\n\ndef _is_form_upload(value: Any) -> bool:\n    return callable(getattr(value, \"read\", None)) and hasattr(value, \"filename\")\n\n\ndef _is_probably_video_upload(value: Any) -> bool:\n    content_type = (getattr(value, \"content_type\", \"\") or \"\").lower()\n    if content_type.startswith(\"video/\"):\n        return True\n    filename = getattr(value, \"filename\", None)\n    if not filename:\n        return False\n    filename = str(filename).split(\"?\", 1)[0].split(\"#\", 1)[0]\n    return os.path.splitext(filename)[1].lower() in _ACTION_VIDEO_EXTENSIONS\n\n","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/entrypoints/action/api.py#L80-L116","documentation":"After successfully parsing the extra_params JSON, the API requires it to be a JSON object (dict). Arrays, strings, numbers, or null JSON values ('[1,2]', '\"x\"', '5') raise ValueError('{field_name} must be a JSON object') -> HTTP 400.","triggerScenarios":"Passing extra_params='[1, 2, 3]' or '\"temperature=0.7\"' (a JSON string) or 'null' in the multipart form.","commonSituations":"Wrapping params in a JSON array; double-encoding so the outer value parses to a scalar or list instead of a dict.","solutions":["Change the value to a JSON object: '{\"key\": \"value\"}'","If you need a list, nest it under a key: '{\"items\": [...]}'"],"exampleFix":"// before\nextra_params=[\"top_k\", 5]\n\n// after\nextra_params={\"top_k\": 5}","handlingStrategy":"type-guard","validationCode":"import json\ndef validate_extra_params(s: str) -> dict:\n    parsed = json.loads(s)\n    if not isinstance(parsed, dict):\n        raise TypeError(\"extra_params must encode a JSON object\")\n    return parsed","typeGuard":"def is_json_object(s: str) -> bool:\n    try:\n        return isinstance(json.loads(s), dict)\n    except (json.JSONDecodeError, TypeError):\n        return False","tryCatchPattern":null,"preventionTips":["Type the field as dict in client code and serialize at the boundary","Wrap lists under a named key instead of sending top-level arrays"],"tags":["api","json","type-validation","http-400"],"backgroundTag":"invalid-json-in-request","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}