run-llama/llama_index · error · ValueError
Cannot provide both content and blocks.
Error message
Cannot provide both content and blocks.
What it means
ToolOutput.__init__ accepts either a plain string content or a list of ContentBlock objects (blocks), but not both. Passing a truthy content AND a non-empty blocks list raises ValueError because the two representations would conflict; internally content is just converted to [TextBlock(text=content)].
Source
Thrown at llama-index-core/llama_index/core/tools/types.py:128
tool_name: str
raw_input: Dict[str, Any]
raw_output: Any
is_error: bool = False
_exception: Optional[Exception] = PrivateAttr(default=None)
def __init__(
self,
tool_name: str,
content: Optional[str] = None,
blocks: Optional[List[ContentBlock]] = None,
raw_input: Optional[Dict[str, Any]] = None,
raw_output: Optional[Any] = None,
is_error: bool = False,
exception: Optional[Exception] = None,
):
if content and blocks:
raise ValueError("Cannot provide both content and blocks.")
if content:
blocks = [TextBlock(text=content)]
elif blocks:
pass
else:
blocks = []
super().__init__(
tool_name=tool_name,
blocks=blocks,
raw_input=raw_input,
raw_output=raw_output,
is_error=is_error,
)
self._exception = exception
@propertyView on GitHub (pinned to afd0fef371)
Solutions
- Pass only one form: either content='text' or blocks=[TextBlock(...), ImageBlock(...)].
- If you need a text summary plus blocks, embed it as a TextBlock inside blocks.
- In wrapper/generic code, choose blocks when the caller supplied blocks, else content.
Example fix
# before
out = ToolOutput(
tool_name='search',
content='hello',
blocks=[TextBlock(text='hello')], # both supplied -> ValueError
)
# after
out = ToolOutput(tool_name='search', blocks=[TextBlock(text='hello')]) Defensive patterns
Strategy: validation
Validate before calling
def build_tool_output(tool_name: str, content=None, blocks=None):
if content and blocks:
raise TypeError('Pass content OR blocks, not both')
if content:
return ToolOutput(tool_name=tool_name, content=content)
return ToolOutput(tool_name=tool_name, blocks=blocks or []) Type guard
def has_conflicting_output_args(content, blocks) -> bool:
return bool(content) and bool(blocks) Try / catch
try:
out = ToolOutput(tool_name=name, content=c, blocks=b)
except ValueError as e:
if 'both content and blocks' in str(e) and b:
out = ToolOutput(tool_name=name, blocks=b) # blocks win
else:
raise Prevention
- Pick one output form per code path; never forward both kwargs in wrappers.
- Convert legacy strings to [TextBlock(text=...)] when migrating to blocks.
- Centralize ToolOutput construction in one helper that enforces the rule.
When it happens
Trigger: Constructing ToolOutput(tool_name='x', content='result text', blocks=[TextBlock(text='result text')]) or subclassing ToolOutput and forwarding both parameters. Also hit by wrapper code that adds a default content string while passing through caller-supplied blocks.
Common situations: Custom tool implementations building rich (multi-block) outputs while keeping a legacy content argument 'just in case'; refactors from string outputs to block outputs that forget to remove the old content kwarg.
Related errors
- spec_functions must be of type: List[Union[str, Tuple[str, s
- Tool name cannot be None
- Invalid additional field info: {field_info}. Must be a tuple
- Max iterations of {max_iterations} reached! Either something
- All agents must have a name in a multi-agent workflow
AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15).
Data as JSON: /api/errors/64981f8780914134.
Report an issue: GitHub.