Skip to content

A context global named request_container replaces the per-message container #74

Closed

Description

@lesnik512

This was generated by AI.

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:

import asyncio

from faststream import ContextRepo, FastStream, TestApp
from faststream.nats import NatsBroker, TestNatsBroker
from modern_di import Container, Group, Scope, providers
from modern_di_faststream import FromDI, setup_di


class Deps(Group):
    value = providers.Factory(scope=Scope.REQUEST, creator=lambda: "dep-value", bound_type=str)


class UserThing:
    def resolve_dependency(self, dep: object) -> str:
        return "VALUE FROM USER'S OWN 'request_container' GLOBAL"


broker = NatsBroker()


@broker.subscriber("subject")
async def handler(msg: str, v: str = FromDI(str)) -> str:
    return v


app = FastStream(broker, context=ContextRepo({"request_container": UserThing()}))
setup_di(app, Container(groups=[Deps]))


async def main() -> None:
    async with TestNatsBroker(broker) as test_broker, TestApp(app):
        print(await (await test_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.

Activity

  1. added
    bugSomething isn't working
    needs-triageMaintainer needs to evaluate this issue
    on Oct 17, 2026
  2. lesnik512 commented on Oct 17, 2026

    @lesnik512
    MemberAuthor

    This was generated by AI during triage.

    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:

    Out of scope:

    • Other public API changes to setup_di or fetch_di_container
  3. added
    ready-for-agentFully specified, ready for an AFK agent
    and removed
    needs-triageMaintainer needs to evaluate this issue
    on Oct 17, 2026
  4. lesnik512 commented on Oct 17, 2026

    @lesnik512
    MemberAuthor

    This was generated by AI during triage.

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingready-for-agentFully specified, ready for an AFK agent

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions