{"record":{"id":"63f69dba9535e77a","repo":"microsoft/semantic-kernel","slug":"city-city-is-not-in-the-list-of-cities","errorCode":null,"errorMessage":"City '{city}' is not in the list of cities: {', '.join(cities)}","messagePattern":"City '(.+?)' is not in the list of cities: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"python/samples/concepts/memory/azure_ai_search_hotel_samples/2_use_as_a_plugin.py","lineNumber":64,"sourceCode":"# This function has to adhere to the `DynamicFilterFunction` signature.\n# which consists of 2 named arguments, `filter`, and `parameters`.\n# and kwargs.\n# It returns the updated filter.\n# The default version that is used when not supplying this, reads the parameters and if there is\n# a parameter that is not `query`, `top`, or 'skip`, and it can find a value for it, either in the kwargs\n# or the default value specified in the parameter, it will add a filter to the options.\n# In this case, we are adding a filter to the options to filter by the city, but since the technical name\n# of that field in the index is `address/city`, want to do this manually.\n# this can also be used to replace a complex technical name in your index with a friendly name towards the LLM.\ndef filter_update(\n    filter: OptionalOneOrList[Callable | str] | None = None,\n    parameters: list[\"KernelParameterMetadata\"] | None = None,\n    **kwargs: Any,\n) -> OptionalOneOrList[Callable | str] | None:\n    if \"city\" in kwargs:\n        city = kwargs[\"city\"]\n        if city not in cities:\n            raise ValueError(f\"City '{city}' is not in the list of cities: {', '.join(cities)}\")\n        # we need the actual value and not a named param, otherwise the parser will not be able to find it.\n        new_filter = f\"lambda x: x.Address.City == '{city}'\"\n        if filter is None:\n            filter = new_filter\n        elif isinstance(filter, list):\n            filter.append(new_filter)\n        else:\n            filter = [filter, new_filter]\n    return filter\n\n\ninstructions = \"\"\"You are a travel agent. Your name is Mosscap and\nyou have one goal: help people find a hotel.\nYour full name, should you need to know it, is\nSplendid Speckled Mosscap. You communicate\neffectively, but you tend to answer with long\nflowery prose. You always make sure to include the\nhotel_id in your answers so that the user can","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/samples/concepts/memory/azure_ai_search_hotel_samples/2_use_as_a_plugin.py#L46-L82","documentation":"ValueError raised by the sample filter_update callback used with the Azure AI Search hotel sample: when a 'city' argument is provided, it validates the value against a fixed list of known cities and refuses unknown values before constructing the search filter. It prevents injecting an arbitrary string into the Azure AI Search OData filter lambda.","triggerScenarios":"The LLM or a caller passes a city value not in the cities list (e.g. a typo, a city not in the index, or an injection attempt) as the 'city' argument to the function whose options hook is filter_update.","commonSituations":"The model hallucinates a city not in the index; a user typo; the cities list was edited but the index was not; or an attempt to abuse the free-text city into the OData filter string.","solutions":["Pass one of the cities listed in the error message (it joins the allowed set).","Expand the cities list to include the requested city only if that city actually exists in the index.","Use the enum/choices pattern so the model only proposes valid cities (annotate the parameter with allowed values).","Sanitize by mapping near-matches to the closest valid city before validation."],"exampleFix":"# before\nif city not in cities:\n    raise ValueError(f\"City '{city}' is not in the list of cities: {', '.join(cities)}\")\n# after - suggest the closest match\nfrom difflib import get_close_matches\nif city not in cities:\n    suggestion = get_close_matches(city, cities, n=1)\n    hint = f\" Did you mean {suggestion[0]!r}?\" if suggestion else \"\"\n    raise ValueError(f\"City '{city}' is not in the list of cities: {', '.join(cities)}{hint}\")","handlingStrategy":"validation","validationCode":"def validate_city(city: str, cities):\n    if city not in cities:\n        from difflib import get_close_matches\n        suggestion = get_close_matches(city, cities, n=1)\n        hint = f\" Did you mean {suggestion[0]!r}?\" if suggestion else \"\"\n        raise ValueError(f\"City '{city}' is not in the list: {', '.join(cities)}{hint}\")\n    return city","typeGuard":"def is_known_city(city: object, cities) -> bool:\n    return isinstance(city, str) and city in cities","tryCatchPattern":"try:\n    result = await kernel.invoke(search_func, city=city)\nexcept ValueError as e:\n    # surface allowed cities back to the model/user\n    print(e)","preventionTips":["Keep the cities list in sync with the actual Azure AI Search index values.","Expose the allowed cities as a Literal/enum parameter so the model only proposes valid values.","Suggest the closest match on rejection to recover quickly."],"tags":["python","sample","azure-ai-search","validation","odata","security"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}