#16997·streamlit

OTEL middleware exception when ran via Streamlit's Starlette but not Uvicorn direct

Author: stefnestorCreated Sep 16, 2026Updated Sep 16, 2026
Labelstype:bugstatus:needs-triagearea:serverarea:integrationsfeature:st.App

Checklist

  • I have searched the existing issues for similar issues.
  • I added a very descriptive title to this issue.
  • I have provided sufficient information below to help reproduce this issue.

Summary

Open Telemetry (OTEL) has a Starlette instrumentation. Streamlit wraps Starlette via st.App (https://github.com/streamlit/streamlit/issues/7667, https://github.com/streamlit/streamlit/issues/13600). Therefore, I expect to be able to OTEL monitor Streamlit (at least API if not also UI). Instead, streamlit run fails to start with AttributeError: 'App' object has no attribute 'add_middleware' where same code works for running via uvicorn.

Reproducible Code Example

from opentelemetry import metrics
from opentelemetry.instrumentation.starlette import StarletteInstrumentor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader, ConsoleMetricExporter
from opentelemetry.sdk.resources import Resource
from starlette import applications
from starlette.middleware import Middleware
from starlette.middleware.trustedhost import TrustedHostMiddleware
from starlette.responses import JSONResponse
from starlette.routing import Route
from streamlit.starlette import App
import streamlit as st


st.markdown("Home")

async def api_health(request):
    return JSONResponse({"status": "healthy"})

### DIFF
## - (A)  FATAL: AttributeError: 'App' object has no attribute 'add_middleware'
app = App(
## - (B)  works but only Starlette API-responsive not Streamlit UI
# app = applications.Starlette(
### 
    "home.py",
    routes=[Route("/api/health", api_health) ],
    middleware=[Middleware(TrustedHostMiddleware, allowed_hosts=['127.0.0.1', 'localhost'])]
)

StarletteInstrumentor.instrument_app(app)
resource = Resource.create(attributes={"service.name": "starlette-service"})
reader = PeriodicExportingMetricReader(ConsoleMetricExporter())
meterProvider = MeterProvider(resource=resource, metric_readers=[reader])
metrics.set_meter_provider(meterProvider)

### RUN
## - (A) python -m streamlit run home.py
##       FATAL: AttributeError: 'App' object has no attribute 'add_middleware'
## - (B) python -m uvicorn home:app 
##      http://localhost:8000/api/health succeeds 
##      after 60s get OTEL metrics logged to console
###

Steps To Reproduce

  1. Install
    $ python --version
    Python 3.14.7
    $ pip install streamlit opentelemetry-sdk opentelemetry-instrumentation-starlette
    $ streamlit --version
    Streamlit, version 1.62.0
    
  2. Toggle+run example code path (A) to see Streamlit error vs (B) to see working Starlette/Uvicorn.

Expected Behavior

I expect Streamlit's app to expose Starlette's add_middleware functionality, especially where middleware was explicitly defined.

Current Behavior

The stack trace appears like

$ python -m streamlit run home.py

  You can now view your Streamlit app in your browser.

  URL: http://localhost:8501

2026-09-16 11:48:39.960 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.
2026-09-16 11:48:39.977
  Warning: to view this Streamlit app on a browser, run it with the following
  command:

    streamlit run /Users/stef/Downloads/Sep16/home.py [ARGUMENTS]
2026-09-16 11:48:39.977 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.
2026-09-16 11:48:39.977 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.
Traceback (most recent call last):
  File "<frozen runpy>", line 203, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/streamlit/__main__.py", line 20, in <module>
    main(prog_name="streamlit")
    ~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/click/core.py", line 1631, in __call__
    return self.main(*args, **kwargs)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/click/core.py", line 1552, in main
    rv = self.invoke(ctx)
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/click/core.py", line 2032, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/click/core.py", line 1415, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/click/core.py", line 910, in invoke
    return callback(*args, **kwargs)
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/streamlit/web/cli.py", line 358, in main_run
    _main_run(path_str, args, flag_options=kwargs)
    ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/streamlit/web/cli.py", line 427, in _main_run
    bootstrap.run_asgi_app(
    ~~~~~~~~~~~~~~~~~~~~~~^
        main_script_path,
        ^^^^^^^^^^^^^^^^^
    ...<2 lines>...
        flag_options,
        ^^^^^^^^^^^^^
    )
    ^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/streamlit/web/bootstrap.py", line 475, in run_asgi_app
    runner.run()
    ~~~~~~~~~~^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/streamlit/web/server/starlette/starlette_server.py", line 602, in run
    server.run(sockets=[server_socket])
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/uvicorn/server.py", line 86, in run
    return asyncio_run(self.serve(sockets=sockets), loop_factory=self.config.get_loop_factory())
  File "/opt/homebrew/Cellar/[email protected]/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/asyncio/runners.py", line 205, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/asyncio/runners.py", line 128, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/asyncio/base_events.py", line 720, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/uvicorn/server.py", line 90, in serve
    await self._serve(sockets)
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/uvicorn/server.py", line 97, in _serve
    config.load()
    ~~~~~~~~~~~^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/uvicorn/config.py", line 511, in load
    self.loaded_app = self.load_app()
                      ~~~~~~~~~~~~~^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/uvicorn/config.py", line 434, in load_app
    return import_from_string(self.app)
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/uvicorn/importer.py", line 19, in import_from_string
    module = importlib.import_module(module_str)
  File "/opt/homebrew/Cellar/[email protected]/3.14.7/Frameworks/Python.framework/Versions/3.14/lib/python3.14/importlib/__init__.py", line 88, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1406, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1371, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 938, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 759, in exec_module
  File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed
  File "/Users/stef/Downloads/Sep16/home.py", line 35, in <module>
    StarletteInstrumentor.instrument_app(app)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
  File "/Users/stef/Library/Python/3.14/lib/python/site-packages/opentelemetry/instrumentation/starlette/__init__.py", line 249, in instrument_app
    app.add_middleware(
    ^^^^^^^^^^^^^^^^^^
AttributeError: 'App' object has no attribute 'add_middleware'

Is this a regression?

  • Yes, this used to work in a previous version.

Debug info

  • Streamlit version: 1.62.0
  • Python version: 3.14.7
  • Operating System: Mac M4 27.0
  • Browser: Firefox 156.0

Additional Information

Use Case: I assume like all of us, I'm just trying to get to working metrics data. The rabbit hole I found myself in is it appears perhaps metrics like this emitted historically under --global.metrics=true but which is now deprecated. So originally started at this Discuss which sublinks to FR https://github.com/streamlit/streamlit/issues/373 and https://github.com/streamlit/streamlit/issues/16730 which led to this Discuss which suggested OTEL (along a custom streamlit_extras build-out) so I tried it with default setup and its Starlette works Uvicorn-layer but not Streamlit-layer where I want/need. TIA!