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

  1. Coerce to string before formatting: `format(url=str(url))`.
  2. Unwrap dicts: if you already have an `ImageURL`, pass `format(url=image_url['url'], detail=image_url.get('detail'))`.
  3. 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

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


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/48fb85de29150d59. Report an issue: GitHub.