{"record":{"id":"c021ab52581108f6","repo":"jumpserver/jumpserver","slug":"path-params-and-query-params-must-be-objects","errorCode":null,"errorMessage":"path_params and query_params must be objects.","messagePattern":"path_params and query_params must be objects\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"apps/chat_ai/executor/request_builder.py","lineNumber":161,"sourceCode":"                    flattened = []\n                    for key, item in value.items():\n                        flattened.extend((str(key), self._query_scalar(item)))\n                    serialized.append((name, ','.join(flattened)))\n                else:\n                    raise ValidationError({f'query_params.{name}': f'Unsupported query style: {style}.'})\n                continue\n\n            serialized.append((name, self._query_scalar(value)))\n        return serialized\n\n    def build(self, operation, arguments):\n        if not isinstance(arguments, dict):\n            raise ValidationError({'arguments': 'Must be an object.'})\n        path_params = arguments.get('path_params') or {}\n        query_params = arguments.get('query_params') or {}\n        body = arguments.get('body', {})\n        if not isinstance(path_params, dict) or not isinstance(query_params, dict):\n            raise ValidationError({'arguments': 'path_params and query_params must be objects.'})\n\n        path = operation.path\n        allowed_path = {item.get('name'): item for item in operation.path_parameters}\n        for name, parameter in allowed_path.items():\n            if parameter.get('required') and name not in path_params:\n                raise ValidationError({'path_params': f'Missing required path parameter: {name}.'})\n            if name in path_params:\n                _validate_scalar(path_params[name], parameter.get('schema') or {}, f'path_params.{name}')\n                path = path.replace('{' + name + '}', quote(str(path_params[name]), safe=''))\n        unknown_path = sorted(set(path_params) - set(allowed_path))\n        if unknown_path or '{' in path or '}' in path:\n            raise ValidationError({'path_params': f'Invalid path parameters: {unknown_path}.'})\n\n        allowed_query = {item.get('name'): item for item in operation.query_parameters}\n        unknown_query = sorted(set(query_params) - set(allowed_query))\n        if unknown_query:\n            raise ValidationError({'query_params': f'Unknown query parameters: {\", \".join(unknown_query)}.'})\n        missing_query = sorted(","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/jumpserver/jumpserver/blob/6ec464fabd61b95912d539455a3a5f15f5c59fe0/apps/chat_ai/executor/request_builder.py#L143-L179","documentation":"Inside the arguments object, the path_params and query_params entries must themselves be dicts (or absent, which defaults to {}). If either is a list, string, scalar, or null-with-truthy issues, build rejects the whole payload with this error before parameter processing starts.","triggerScenarios":"arguments = {'path_params': ['/users', '123']} or {'query_params': 'limit=10'} — i.e. the nested containers are not objects. Also {'query_params': None} combined with a truthy path_params is fine (None falls back to {}), but any non-dict truthy value fails.","commonSituations":"LLMs emitting query strings instead of objects; specs where the model confuses the flat and nested argument layouts; hand-built test fixtures using tuples/lists of pairs.","solutions":["Make path_params and query_params dicts of name→value, e.g. {'path_params': {'user_id': 123}, 'query_params': {'limit': 10}}","Parse query-string style input into a dict (urllib.parse.parse_qs) before passing it in","Adjust the tool's argument schema so the model is forced to emit objects for these fields"],"exampleFix":"# before\n{'path_params': ['user_id', 123], 'query_params': 'limit=10'}\n\n# after\n{'path_params': {'user_id': 123}, 'query_params': {'limit': 10}}","handlingStrategy":"type-guard","validationCode":"pp = arguments.get('path_params') or {}\nqp = arguments.get('query_params') or {}\nassert isinstance(pp, dict) and isinstance(qp, dict), 'path_params/query_params must be objects'","typeGuard":"def has_dict_param_containers(arguments) -> bool:\n    return isinstance(arguments.get('path_params') or {}, dict) and isinstance(arguments.get('query_params') or {}, dict)","tryCatchPattern":"try:\n    builder.build(operation, arguments)\nexcept ValidationError as e:\n    if 'must be objects' in e.errors.get('arguments', ''):\n        arguments['query_params'] = dict(arguments['query_params'])  # e.g. from list of pairs\n        result = builder.build(operation, arguments)\n    else:\n        raise\n","preventionTips":["Use {\"path_params\": {...}, \"query_params\": {...}} shapes everywhere","Convert query strings with parse_qs before passing them in","Pin the expected argument layout in tool descriptions"],"tags":["validation","path-params","query-params","request-builder"],"backgroundTag":"invalid-request-payload-shape","analyzedSha":"6ec464fabd61b95912d539455a3a5f15f5c59fe0","analyzedAt":"2026-08-28T11:33:00.925Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}