fix(core): add messages to bare raise ValueError calls (#38158)

Closes #35727

---

Several internal code paths raised a bare `ValueError` with no message,
so when one of these conditions tripped, users saw a traceback with no
explanation of what actually went wrong.

This adds descriptive messages to each of those `raise ValueError`
calls:

- `FewShotPromptWithTemplates._get_examples` / `_aget_examples` — when
neither `examples` nor `example_selector` is set.
- Prompt `loading` — when a referenced template file uses an unsupported
(non-`.txt`) format.
- The LangSmith document loader — when both `client` and `client_kwargs`
are supplied.
- The Anthropic file-search middleware brace expansion — for unbalanced
or empty brace patterns.

Made by [Open SWE](https://openswe.vercel.app)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
Mason Daugherty
2026-06-22 23:02:05 -04:00
committed by GitHub
parent dae4e5de9d
commit a5a8f8db78
7 changed files with 47 additions and 8 deletions

View File

@@ -94,7 +94,8 @@ class LangSmithLoader(BaseLoader):
ValueError: If both `client` and `client_kwargs` are provided.
""" # noqa: E501
if client and client_kwargs:
raise ValueError
msg = "Only one of 'client' and 'client_kwargs' should be provided."
raise ValueError(msg)
self._client = client or LangSmithClient(**client_kwargs)
self.content_key = list(content_key.split(".")) if content_key else []
self.format_content = format_content or _stringify

View File

@@ -111,14 +111,16 @@ class FewShotPromptWithTemplates(StringPromptTemplate):
return self.examples
if self.example_selector is not None:
return self.example_selector.select_examples(kwargs)
raise ValueError
msg = "One of 'examples' and 'example_selector' should be provided"
raise ValueError(msg)
async def _aget_examples(self, **kwargs: Any) -> list[dict[str, Any]]:
if self.examples is not None:
return self.examples
if self.example_selector is not None:
return await self.example_selector.aselect_examples(kwargs)
raise ValueError
msg = "One of 'examples' and 'example_selector' should be provided"
raise ValueError(msg)
def format(self, **kwargs: Any) -> str:
"""Format the prompt with the inputs.

View File

@@ -104,7 +104,11 @@ def _load_template(
if resolved_path.suffix == ".txt":
template = resolved_path.read_text(encoding="utf-8")
else:
raise ValueError
msg = (
f"Unsupported template file format: '{resolved_path.suffix}'. "
"Only '.txt' files are supported."
)
raise ValueError(msg)
# Set the template variable to the extracted variable.
config[var_name] = template
return config

View File

@@ -2,6 +2,7 @@ import datetime
import uuid
from unittest.mock import MagicMock, patch
import pytest
from langsmith.schemas import Example
from langchain_core.document_loaders import LangSmithLoader
@@ -13,6 +14,12 @@ def test_init() -> None:
LangSmithLoader(api_key="secret")
def test_init_client_and_client_kwargs_conflict() -> None:
"""Passing both `client` and `client_kwargs` should raise."""
with pytest.raises(ValueError, match="Only one of 'client' and 'client_kwargs'"):
LangSmithLoader(client=MagicMock(), api_key="secret")
EXAMPLES = [
Example(
inputs={"first": {"second": "foo"}},

View File

@@ -81,3 +81,26 @@ def test_prompttemplate_validation() -> None:
example_prompt=EXAMPLE_PROMPT,
example_separator="\n",
).input_variables == ["content", "new_content"]
async def test_get_examples_requires_examples_or_selector() -> None:
"""Both `_get_examples` and `_aget_examples` raise when neither is set.
The constructor validator forbids the neither-provided case, so the fields
are cleared after construction to reach the guard inside the getters.
"""
suffix = PromptTemplate(input_variables=[], template="end")
prompt = FewShotPromptWithTemplates(
suffix=suffix,
input_variables=[],
examples=[{"question": "foo", "answer": "bar"}],
example_prompt=EXAMPLE_PROMPT,
)
prompt.examples = None
prompt.example_selector = None
match = "One of 'examples' and 'example_selector' should be provided"
with pytest.raises(ValueError, match=match):
prompt._get_examples()
with pytest.raises(ValueError, match=match):
await prompt._aget_examples()

View File

@@ -316,7 +316,7 @@ def test_symlink_txt_to_py_is_blocked(tmp_path: Path) -> None:
os.chdir(tmp_path)
with (
suppress_langchain_deprecation_warning(),
pytest.raises(ValueError), # noqa: PT011
pytest.raises(ValueError, match="files are supported"),
):
load_prompt_from_config(config)
finally:
@@ -341,7 +341,7 @@ def test_symlink_jinja2_rce_is_blocked(tmp_path: Path) -> None:
}
with (
suppress_langchain_deprecation_warning(),
pytest.raises(ValueError), # noqa: PT011
pytest.raises(ValueError, match="files are supported"),
):
load_prompt_from_config(config, allow_dangerous_paths=True)

View File

@@ -35,13 +35,15 @@ def _expand_include_patterns(pattern: str) -> list[str] | None:
end = current.find("}", start)
if end == -1:
raise ValueError
msg = f"Unbalanced brace in pattern: '{current}' is missing a '}}'."
raise ValueError(msg)
prefix = current[:start]
suffix = current[end + 1 :]
inner = current[start + 1 : end]
if not inner:
raise ValueError
msg = f"Empty brace expansion in pattern: '{current}'."
raise ValueError(msg)
for option in inner.split(","):
_expand(prefix + option + suffix)