{"record":{"id":"48fb85de29150d59","repo":"langchain-ai/langchain","slug":"url-must-be-a-string","errorCode":null,"errorMessage":"url must be a string.","messagePattern":"url must be a string\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/prompts/image.py","lineNumber":147,"sourceCode":"                formatted[k] = DEFAULT_FORMATTER_MAPPING[self.template_format](\n                    v, **kwargs\n                )\n            else:\n                formatted[k] = v\n        url = kwargs.get(\"url\") or formatted.get(\"url\")\n        if kwargs.get(\"path\") or formatted.get(\"path\"):\n            msg = (\n                \"Loading images from 'path' has been removed as of 0.3.15 for security \"\n                \"reasons. Please specify images by 'url'.\"\n            )\n            raise ValueError(msg)\n        detail = kwargs.get(\"detail\") or formatted.get(\"detail\")\n        if not url:\n            msg = \"Must provide url.\"\n            raise ValueError(msg)\n        if not isinstance(url, str):\n            msg = \"url must be a string.\"\n            raise ValueError(msg)  # noqa: TRY004\n        output: ImageURL = {\"url\": url}\n        if detail:\n            # Don't check literal values here: let the API check them\n            output[\"detail\"] = cast(\"Literal['auto', 'low', 'high']\", detail)\n        return output\n\n    async def aformat(self, **kwargs: Any) -> ImageURL:\n        \"\"\"Async format the prompt with the inputs.\n\n        Args:\n            **kwargs: Any arguments to be passed to the prompt template.\n\n        Returns:\n            A formatted string.\n        \"\"\"\n        return await run_in_executor(None, self.format, **kwargs)\n\n    def pretty_repr(","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/prompts/image.py#L129-L165","documentation":"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.","triggerScenarios":"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.","commonSituations":"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`.","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."],"exampleFix":"# before\nprompt.format(url=Path('/tmp/img.png'))  # ValueError: url must be a string.\n\n# after\nprompt.format(url='data:image/png;base64,...')  # str url\n# or\nprompt.format(url=str(some_value))","handlingStrategy":"type-guard","validationCode":"url = kwargs.get('url', template.get('url'))\nif url is not None and not isinstance(url, str):\n    kwargs['url'] = str(url)\nout = prompt.format(**kwargs)","typeGuard":"from typing import Any\n\ndef is_string_url(value: Any) -> bool:\n    return isinstance(value, str)","tryCatchPattern":null,"preventionTips":["Convert Path objects and dict payloads to str at the boundary of your prompt pipeline.","Unwrap ImageURL dicts before passing them back into format()."],"tags":["prompts","image","type-error","multimodal"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}