fix: Replace threading.local() with contextvars for async-safe A2A request context #637
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Problem
A2A server was using
threading.local()to store request context (auth token and headers), which doesn't work properly with async/await code. Thread-local storage is thread-specific, but async functions can execute on different threads, causing context variables to become inaccessible in async handlers.This manifested as 'No tenant context set' errors in
list_authorized_propertiesand other A2A endpoints because:_request_context.request_headersin one threadgetattr(_request_context, 'request_headers', {})get_current_tenant()raised RuntimeErrorSolution
Replace
threading.local()withcontextvars.ContextVar, which properly handles async contexts:Before:
```python
_request_context = threading.local()
_request_context.auth_token = token
_request_context.request_headers = dict(request.headers)
token = getattr(_request_context, 'auth_token', None)
```
After:
```python
_request_auth_token: contextvars.ContextVar[str | None] = contextvars.ContextVar('request_auth_token', default=None)
_request_headers: contextvars.ContextVar[dict | None] = contextvars.ContextVar('request_headers', default=None)
_request_auth_token.set(token)
_request_headers.set(dict(request.headers))
token = _request_auth_token.get()
```
Changes
Production Code
threading.local()with two ContextVars.get()and.set()methods.set(None)instead ofdelattr()Test Code
adcp_a2a_server._request_headers.set()instead of mockingSchemas
Testing
References
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com