microsoft/semantic-kernel · warning · ValueError
City '{city}' is not in the list of cities: {', '.join(citie
Error message
City '{city}' is not in the list of cities: {', '.join(cities)} What it means
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.
Source
Thrown at python/samples/concepts/memory/azure_ai_search_hotel_samples/2_use_as_a_plugin.py:64
# This function has to adhere to the `DynamicFilterFunction` signature.
# which consists of 2 named arguments, `filter`, and `parameters`.
# and kwargs.
# It returns the updated filter.
# The default version that is used when not supplying this, reads the parameters and if there is
# a parameter that is not `query`, `top`, or 'skip`, and it can find a value for it, either in the kwargs
# or the default value specified in the parameter, it will add a filter to the options.
# In this case, we are adding a filter to the options to filter by the city, but since the technical name
# of that field in the index is `address/city`, want to do this manually.
# this can also be used to replace a complex technical name in your index with a friendly name towards the LLM.
def filter_update(
filter: OptionalOneOrList[Callable | str] | None = None,
parameters: list["KernelParameterMetadata"] | None = None,
**kwargs: Any,
) -> OptionalOneOrList[Callable | str] | None:
if "city" in kwargs:
city = kwargs["city"]
if city not in cities:
raise ValueError(f"City '{city}' is not in the list of cities: {', '.join(cities)}")
# we need the actual value and not a named param, otherwise the parser will not be able to find it.
new_filter = f"lambda x: x.Address.City == '{city}'"
if filter is None:
filter = new_filter
elif isinstance(filter, list):
filter.append(new_filter)
else:
filter = [filter, new_filter]
return filter
instructions = """You are a travel agent. Your name is Mosscap and
you have one goal: help people find a hotel.
Your full name, should you need to know it, is
Splendid Speckled Mosscap. You communicate
effectively, but you tend to answer with long
flowery prose. You always make sure to include the
hotel_id in your answers so that the user canView on GitHub (pinned to c028a0c7dc)
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.
Example fix
# before
if city not in cities:
raise ValueError(f"City '{city}' is not in the list of cities: {', '.join(cities)}")
# after - suggest the closest match
from difflib import get_close_matches
if city not in cities:
suggestion = get_close_matches(city, cities, n=1)
hint = f" Did you mean {suggestion[0]!r}?" if suggestion else ""
raise ValueError(f"City '{city}' is not in the list of cities: {', '.join(cities)}{hint}") Defensive patterns
Strategy: validation
Validate before calling
def validate_city(city: str, cities):
if city not in cities:
from difflib import get_close_matches
suggestion = get_close_matches(city, cities, n=1)
hint = f" Did you mean {suggestion[0]!r}?" if suggestion else ""
raise ValueError(f"City '{city}' is not in the list: {', '.join(cities)}{hint}")
return city Type guard
def is_known_city(city: object, cities) -> bool:
return isinstance(city, str) and city in cities Try / catch
try:
result = await kernel.invoke(search_func, city=city)
except ValueError as e:
# surface allowed cities back to the model/user
print(e) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid operator
- Invalid filename: would write outside the expected directory
- Invalid filename: would write outside the expected directory
- Radius and height must be non-negative.
- Azure AI Search tool definition must have both 'index_connec
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/63f69dba9535e77a.
Report an issue: GitHub.