deepset-ai/haystack · error · RuntimeError

Tool '{tool.name}': failed to merge outputs into state. {e}

Error message

Tool '{tool.name}': failed to merge outputs into state. {e}

What it means

_merge_tool_outputs_into_state wraps any exception from State.set in this RuntimeError, chained with the original cause. It signals that a tool's output could not be merged into agent state — typically because the target state key is not in the schema or the handler failed.

Source

Thrown at haystack/components/agents/tool_calling.py:80

def _merge_tool_outputs_into_state(tool: Tool, result: Any, state: State) -> None:
    """
    Write tool outputs into State according to the tool's `outputs_to_state` mapping.

    :raises RuntimeError: If writing an output value into the state fails.
    """
    if not isinstance(result, dict):
        return

    for state_key, config in (tool.outputs_to_state or {}).items():
        source_key = config.get("source", None)
        if source_key and source_key not in result:
            continue
        output_value = result.get(source_key) if source_key else result
        try:
            state.set(state_key, output_value, handler_override=config.get("handler"))
        except Exception as e:
            raise RuntimeError(f"Tool '{tool.name}': failed to merge outputs into state. {e}") from e


def _result_to_string(result: Any) -> str:
    """
    Convert a tool result to a string.

    Strings are returned as-is; all other types are passed through a JSON serialization step to produce more readable
    output, with a fallback to plain str() conversion if serialization fails.

    :param result: The tool result to convert.
    :returns: A string representation of the tool result.
    """
    if isinstance(result, str):
        return result
    serializable = _serializable_value(value=result, use_placeholders=False)
    try:
        return json.dumps(serializable, ensure_ascii=False)
    except Exception as error:

View on GitHub (pinned to e318778c9b)

Solutions

  1. Read the chained cause (e) to see the underlying State error
  2. Add the missing key to the Agent's state_schema or fix the tool's output state_key
  3. Test the custom handler against the tool's actual output shape

Example fix

// before
tool = Tool(..., outputs_to_state={"summary": {"source_key": "sum"}})
# state_schema lacks "summary"
// after
agent = Agent(..., state_schema={..., "summary": {"type": str}})
Defensive patterns

Strategy: try-catch

Validate before calling

def output_keys_declared(tool, schema):
    for state_key in tool.outputs_to_state or {}:
        if state_key not in schema:
            raise ValueError(f"State key '{state_key}' missing from schema")

Type guard

def can_merge(tool, schema) -> bool:
    return all(k in schema for k in (tool.outputs_to_state or {}))

Try / catch

try:
    finalize_tool_result(tool, result, state, config)
except RuntimeError as e:
    if "failed to merge outputs into state" in str(e):
        logging.warning("State merge failed for %s: %s", tool.name, e.__cause__)
        # fall back to returning tool output as string only
    else:
        raise

Prevention

When it happens

Trigger: A tool's output mapping references a state_key absent from the schema (inner ValueError), or the configured handler raises inside state.set.

Common situations: Mismatch between tool output config and Agent state_schema after refactoring; a custom handler raising on unexpected value shapes.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/d4edcc27e9df51cb. Report an issue: GitHub.