You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A context global named request_container replaces the per-message container #74
The middleware stores each message's child container in the FastStream context with context.scope("request_container", ...) and reads it back by that name. ContextRepo.get checks globals before locals, so if the app's context already has a global named request_container, FromDI resolves from the user's object instead of the per-message container, without any error.
Repro on main (9e85e48), faststream 0.7.4, modern-di 4.1.0, Python 3.14.7:
importasynciofromfaststreamimportContextRepo, FastStream, TestAppfromfaststream.natsimportNatsBroker, TestNatsBrokerfrommodern_diimportContainer, Group, Scope, providersfrommodern_di_faststreamimportFromDI, setup_diclassDeps(Group):
value=providers.Factory(scope=Scope.REQUEST, creator=lambda: "dep-value", bound_type=str)
classUserThing:
defresolve_dependency(self, dep: object) ->str:
return"VALUE FROM USER'S OWN 'request_container' GLOBAL"broker=NatsBroker()
@broker.subscriber("subject")asyncdefhandler(msg: str, v: str=FromDI(str)) ->str:
returnvapp=FastStream(broker, context=ContextRepo({"request_container": UserThing()}))
setup_di(app, Container(groups=[Deps]))
asyncdefmain() ->None:
asyncwithTestNatsBroker(broker) astest_broker, TestApp(app):
print(await (awaittest_broker.request("x", "subject")).decode())
asyncio.run(main()) # VALUE FROM USER'S OWN 'request_container' GLOBAL
Suggested fix: use a namespaced key such as modern_di_request_container, and read it with context.get_local so a global cannot shadow it. See also #75, the unprefixed di_container global.
Verified on main (9e85e48): the handler returns the user's global instead of the per-message dependency. Both context names are private constants (fetch_di_container is the public way to reach the root container), so renaming them is not a breaking change. This brief covers #75 too, since the fix is the same.
Agent Brief
Category: bug Summary: the plugin's FastStream context entries must not collide with user context names
Current behavior:
The middleware stores each message's child container as request_container with context.scope and reads it back by that name. ContextRepo.get checks globals before locals, so a user global named request_container silently replaces the per-message container. Separately, setup_di stores the root container as the global di_container, and a user global of that name set after setup_di replaces what fetch_di_container returns.
Desired behavior:
Both entries use namespaced names that a user is unlikely to pick (for example prefixed with modern_di_). The per-message container is read in a way a global cannot shadow, such as context.get_local. A user's request_container or di_container global no longer affects resolution or fetch_di_container.
Key interfaces:
The middleware's per-message context entry and the lookup used by FromDI resolution.
setup_di and fetch_di_container(app): same public behavior, new internal key.
Acceptance criteria:
With a context global request_container set by the user, a handler's FromDI still gets the per-message dependency
With a global di_container set after setup_di, fetch_di_container(app) still returns the container passed to setup_di
Existing tests pass unchanged
Tests cover both cases, written to fail before the fix
Update: the repro in the issue body shows the bug on FastStream 0.7.4 but prints dep-value on 0.7.7. On 0.7.7 a broker attached to an app gets a context composition, and the middleware's scope() writes into the inner repo, which is searched before the app's ContextRepo. The collision still happens when the global lands in that inner repo, for example through broker.context.set_global("request_container", ...) or the context injected into a handler. The test in #76 uses broker.context.set_global.
Activity
lesnik512 commentedon Oct 17, 2026
Verified on
main(9e85e48): the handler returns the user's global instead of the per-message dependency. Both context names are private constants (fetch_di_containeris the public way to reach the root container), so renaming them is not a breaking change. This brief covers #75 too, since the fix is the same.Agent Brief
Category: bug
Summary: the plugin's FastStream context entries must not collide with user context names
Current behavior:
The middleware stores each message's child container as
request_containerwithcontext.scopeand reads it back by that name.ContextRepo.getchecks globals before locals, so a user global namedrequest_containersilently replaces the per-message container. Separately,setup_distores the root container as the globaldi_container, and a user global of that name set aftersetup_direplaces whatfetch_di_containerreturns.Desired behavior:
Both entries use namespaced names that a user is unlikely to pick (for example prefixed with
modern_di_). The per-message container is read in a way a global cannot shadow, such ascontext.get_local. A user'srequest_containerordi_containerglobal no longer affects resolution orfetch_di_container.Key interfaces:
FromDIresolution.setup_diandfetch_di_container(app): same public behavior, new internal key.Acceptance criteria:
request_containerset by the user, a handler'sFromDIstill gets the per-message dependencydi_containerset aftersetup_di,fetch_di_container(app)still returns the container passed tosetup_diOut of scope:
setup_diorfetch_di_containerlesnik512 commentedon Oct 17, 2026
Update: the repro in the issue body shows the bug on FastStream 0.7.4 but prints
dep-valueon 0.7.7. On 0.7.7 a broker attached to an app gets a context composition, and the middleware'sscope()writes into the inner repo, which is searched before the app'sContextRepo. The collision still happens when the global lands in that inner repo, for example throughbroker.context.set_global("request_container", ...)or thecontextinjected into a handler. The test in #76 usesbroker.context.set_global.