{"record":{"id":"f02080640102d3f9","repo":"PrefectHQ/fastmcp","slug":"query-parameters-invalid-query-params-must-be-op","errorCode":null,"errorMessage":"Query parameters {invalid_query_params} must be optional function parameters with default values","messagePattern":"Query parameters (.+?) must be optional function parameters with default values","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/resources/template.py","lineNumber":516,"sourceCode":"        required_params = {\n            p\n            for p in func_params\n            if user_sig.parameters[p].default is inspect.Parameter.empty\n            and user_sig.parameters[p].kind != inspect.Parameter.VAR_KEYWORD\n        }\n        optional_params = {\n            p\n            for p in func_params\n            if user_sig.parameters[p].default is not inspect.Parameter.empty\n            and user_sig.parameters[p].kind != inspect.Parameter.VAR_KEYWORD\n        }\n\n        # Validate RFC 6570 query parameters\n        # Query params must be optional (have defaults)\n        if query_params:\n            invalid_query_params = query_params - optional_params\n            if invalid_query_params:\n                raise ValueError(\n                    f\"Query parameters {invalid_query_params} must be optional function parameters with default values\"\n                )\n\n        # Check if required parameters are a subset of the path parameters\n        if not required_params.issubset(path_params):\n            raise ValueError(\n                f\"Required function arguments {required_params} must be a subset of the URI path parameters {path_params}\"\n            )\n\n        # Check if all URI parameters are valid function parameters (skip if **kwargs present)\n        if not any(\n            param.kind == inspect.Parameter.VAR_KEYWORD\n            for param in sig.parameters.values()\n        ):\n            if not all_uri_params.issubset(func_params):\n                raise ValueError(\n                    f\"URI parameters {all_uri_params} must be a subset of the function arguments: {func_params}\"\n                )","sourceCodeStart":498,"sourceCodeEnd":534,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/resources/template.py#L498-L534","documentation":"RFC 6570 query parameters (?name={value}) are optional by nature - a client may omit them. To support omission, FastMCP requires every function parameter bound to a query parameter to have a default value. Required (no-default) parameters bound to the query section raise this error.","triggerScenarios":"Registering a template where a parameter appearing only in the URI query part (e.g. ?limit={limit}) maps to a function parameter without a default, like def fn(key: str, limit: int).","commonSituations":"Adding an optional query filter but declaring the function param as required; moving a parameter from the path to the query section without adding a default; stricter signatures after refactoring.","solutions":["Give the query-bound function parameter a default value (e.g. limit: int = 10)","Move the parameter into the URI path section if it must be required","Remove the parameter from both the URI and the function if it isn't needed"],"exampleFix":"// before\ndef list_items(key: str, limit: int): ...\ntemplate(uri_template=\"items://{key}?limit={limit}\")\n// after\ndef list_items(key: str, limit: int = 10): ...\ntemplate(uri_template=\"items://{key}?limit={limit}\")","handlingStrategy":"validation","validationCode":"import inspect, re\ndef check_query_params(fn, uri_template):\n    query = uri_template.split(\"?\", 1)[-1]\n    qparams = {p.replace(\"-\", \"_\") for p in re.findall(r\"\\{(\\w[-\\w]*)\\}\", query)}\n    sig = inspect.signature(fn)\n    for name in qparams:\n        p = sig.parameters.get(name)\n        if p is None or p.default is inspect.Parameter.empty:\n            raise TypeError(f\"query param {name} needs a default value\")","typeGuard":"def query_params_optional(fn, uri_template) -> bool:\n    import inspect, re\n    query = uri_template.split(\"?\", 1)[-1]\n    qp = {p.replace(\"-\", \"_\") for p in re.findall(r\"\\{(\\w[-\\w]*)\\}\", query)}\n    params = inspect.signature(fn).parameters\n    return all(n in params and params[n].default is not inspect.Parameter.empty for n in qp)","tryCatchPattern":"try:\n    tpl = FunctionResourceTemplate.from_function(fn, uri_template=uri)\nexcept ValueError as e:\n    if \"must be optional\" in str(e):\n        fn = add_defaults(fn)  # bind defaults for query-bound params\n        tpl = FunctionResourceTemplate.from_function(fn, uri_template=uri)\n    else:\n        raise","preventionTips":["Give every query-bound parameter a sensible default","Keep required data in the path, optional data in the query","Review function signatures whenever moving params between path and query"],"tags":["uri-template","query-params","validation"],"backgroundTag":"required-query-parameter","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}