langchain-ai/langchain · error · ValueError
url must be a string.
Error message
url must be a string.
What it means
After confirming a url exists, `ImagePromptTemplate.format()` type-checks it: if the resolved url is not a `str` (e.g. a dict, list, or `Path` object interpolated into the template), it raises `ValueError('url must be a string.')`. The downstream `ImageURL` TypedDict requires a string url.
Source
Thrown at libs/core/langchain_core/prompts/image.py:147
formatted[k] = DEFAULT_FORMATTER_MAPPING[self.template_format](
v, **kwargs
)
else:
formatted[k] = v
url = kwargs.get("url") or formatted.get("url")
if kwargs.get("path") or formatted.get("path"):
msg = (
"Loading images from 'path' has been removed as of 0.3.15 for security "
"reasons. Please specify images by 'url'."
)
raise ValueError(msg)
detail = kwargs.get("detail") or formatted.get("detail")
if not url:
msg = "Must provide url."
raise ValueError(msg)
if not isinstance(url, str):
msg = "url must be a string."
raise ValueError(msg) # noqa: TRY004
output: ImageURL = {"url": url}
if detail:
# Don't check literal values here: let the API check them
output["detail"] = cast("Literal['auto', 'low', 'high']", detail)
return output
async def aformat(self, **kwargs: Any) -> ImageURL:
"""Async format the prompt with the inputs.
Args:
**kwargs: Any arguments to be passed to the prompt template.
Returns:
A formatted string.
"""
return await run_in_executor(None, self.format, **kwargs)
def pretty_repr(View on GitHub (pinned to e32fa9a52e)
Solutions
- Coerce to string before formatting: `format(url=str(url))`.
- Unwrap dicts: if you already have an `ImageURL`, pass `format(url=image_url['url'], detail=image_url.get('detail'))`.
- Convert `Path` objects with `.as_uri()` or `str()` at the call site.
Example fix
# before
prompt.format(url=Path('/tmp/img.png')) # ValueError: url must be a string.
# after
prompt.format(url='data:image/png;base64,...') # str url
# or
prompt.format(url=str(some_value)) Defensive patterns
Strategy: type-guard
Validate before calling
url = kwargs.get('url', template.get('url'))
if url is not None and not isinstance(url, str):
kwargs['url'] = str(url)
out = prompt.format(**kwargs) Type guard
from typing import Any
def is_string_url(value: Any) -> bool:
return isinstance(value, str) Prevention
- Convert Path objects and dict payloads to str at the boundary of your prompt pipeline.
- Unwrap ImageURL dicts before passing them back into format().
When it happens
Trigger: Calling `format(url={'url': 'https://...'})` (nesting by mistake), `format(url=Path('/a/b.png'))`, or a template variable rendering to a non-string object via f-string formatting of a complex value.
Common situations: Wrapping the url in an extra dict/`ImageURL` before passing it in; passing a `pathlib.Path` instead of `str(path)`; prompt pipelines that forward raw tool output objects into `format`.
Related errors
- input_variables for the image template cannot contain any of
- Loading images from 'path' has been removed as of 0.3.15 for
- Must provide url.
- Only one format variable allowed per image template.\nGot: {
- Invalid image template: {tmpl}
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/48fb85de29150d59.
Report an issue: GitHub.