Skip to content

fix: raise a clear RuntimeError when inject runs without the DI interceptor - #21

Merged
lesnik512 merged 1 commit into
mainfrom
fix/clear-error-without-interceptor
Sep 15, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
fix/clear-error-without-interceptor

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Problem

An @inject-decorated servicer method (or a fetch_di_container() call) running on a server built without DIInterceptor / DIAioInterceptor failed with the raw ContextVar lookup error, which names nothing about the mistake or the fix:

LookupError: <ContextVar name='modern_di_request_container' at 0x...>

Over the wire the client saw StatusCode.UNKNOWN with details Exception calling application: <ContextVar name='modern_di_request_container' at 0x...> (sync) / Unexpected <class 'LookupError'>: <ContextVar ...> (aio).

Change

fetch_di_container and _resolve (the @inject path) now read the per-RPC child through one private helper, _current_container, which catches the LookupError and raises a plain RuntimeError from None:

No modern-di container found for this RPC. Add DIInterceptor (sync server) or DIAioInterceptor (aio server) to the server's interceptors so RPCs pass through it before using @Inject or fetch_di_container.

Behaviour with the interceptor in place is unchanged. The fetch_di_container docstring and the README API row are updated to say RuntimeError.

Behaviour change

fetch_di_container() outside an intercepted RPC now raises RuntimeError instead of LookupError. The one test that pinned LookupError (test_fetch_di_container_raises_outside_rpc) is changed to the new RuntimeError. The two white-box tests that assert LookupError directly on _request_container.get() are untouched, since the ContextVar itself is unchanged.

This follows the aiohttp/starlette/aiogram integrations (modern-python/modern-di-aiogram#22) and the integration-guide rule in modern-python/modern-di#490: a package-local RuntimeError, not a ModernDIError subclass.

Tests

  • tests/test_inject.py::test_inject_raises_without_interceptor (new): @inject method called with the ContextVar unset raises RuntimeError matching DIInterceptor.
  • tests/test_inject.py::test_fetch_di_container_raises_outside_rpc: now expects RuntimeError matching DIInterceptor.
  • tests/test_sync.py::test_inject_without_interceptor_reports_missing_interceptor (new): a real grpc.server with no interceptor; the client gets StatusCode.UNKNOWN with DIInterceptor in the details.

All three were written first and failed with the original LookupError before the fix. just lint-ci and just test-ci pass at 100% coverage.

@lesnik512
lesnik512 merged commit 8680d83 into main Sep 15, 2026
7 checks passed
@lesnik512
lesnik512 deleted the fix/clear-error-without-interceptor branch September 15, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant