Skip to content

[Bug] dispatcher.py task factory doesn't allow for optional context parameter #1508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jreed-Aces opened this issue Jun 10, 2024 · 19 comments · Fixed by #1523
Closed

[Bug] dispatcher.py task factory doesn't allow for optional context parameter #1508

jreed-Aces opened this issue Jun 10, 2024 · 19 comments · Fixed by #1523
Assignees

Comments

@jreed-Aces
Copy link

Expected Behavior

Python 3.11 allows for a named context parameter to be passed in when constructing a task (https://github.com/python/cpython/blob/3.11/Lib/asyncio/tasks.py#L106). However, the task factory that creates a ContextEnabledTask inside of dispatcher.py doesn't allow for that parameter. When a task is added that attempts to utilize the parameter, an error is raised instead:

System.Private.CoreLib: Exception while executing function: Functions.mention_queue_trigger. System.Private.CoreLib: Result: Failure
Exception: TypeError: Dispatcher.dispatch_forever.<locals>.<lambda>() got an unexpected keyword argument 'context'
...
File "~\Python\Python311\Lib\asyncio\base_events.py", line 449, in create_task
    task = self._task_factory(self, coro, context=context)

Actual Behavior

The task_factory lambda and the ContextEnabledTask should be able to accept the optional, named parameter of context

Steps to Reproduce

Using the existing task_factory, attempt to add a new task along with the named context parameter.

Relevant code being tried

The following fix appears to resolve the issue:


+++ b/azure_functions_worker/dispatcher.py
@@ -141,7 +141,7 @@ class Dispatcher(metaclass=DispatcherMeta):
                         worker_id=self.worker_id)))

             self._loop.set_task_factory(
-                lambda loop, coro: ContextEnabledTask(coro, loop=loop))
+                lambda loop, coro, context=None: ContextEnabledTask(coro, loop=loop, context=context))

             # Detach console logging before enabling GRPC channel logging
             logger.info('Detaching console logging.')
@@ -849,8 +849,8 @@ class AsyncLoggingHandler(logging.Handler):
 class ContextEnabledTask(asyncio.Task):
     AZURE_INVOCATION_ID = '__azure_function_invocation_id__'

-    def __init__(self, coro, loop):
-        super().__init__(coro, loop=loop)
+    def __init__(self, coro, loop, context=None):
+        super().__init__(coro, loop=loop, context = context)

         current_task = asyncio.current_task(loop)
         if current_task is not None:


### Relevant log output

_No response_

### requirements.txt file

_No response_

### Where are you facing this problem?

Local - Core Tools

### Function app name

_No response_

### Additional Information

_No response_
@brbarnett
Copy link

@jreed-Aces have you found a workaround for this? I'm having the same issue right now and you just happened to create this 2 days ago 🙂

@jreed-Aces
Copy link
Author

@brbarnett We downgraded to Python 3.10, which doesn't have the optional context parameter. It fixes the issue, but it's far from ideal.

Locally the code fix I posted above works, but that doesn't help when deploying to Azure.

@Darcos923
Copy link

I have the same issue with py 3.11... Fixed for the moment with py 3.10 version

@dslam
Copy link

dslam commented Jun 21, 2024

ran into the same issue with python 3.11 when working on http stream mode on azure function app

@TobiConti
Copy link

run into the same issue using langchain Runnable method ainvoke. This worked for me but don't know why:

loop = asyncio.get_running_loop()
loop.set_task_factory(None)

@MarthinusBosman
Copy link

Any update on this? I see there's an open PR to solve it since more than a month ago? And @TobiConti's solution doesn't really work for me within an Azure functions environment

@hallvictoria
Copy link
Contributor

The fix has been merged and will be in the next worker release. Thanks for your patience!

@tmusbi
Copy link

tmusbi commented Aug 19, 2024

The fix has been merged and will be in the next worker release. Thanks for your patience!

any timeline for the next release?

@hallvictoria
Copy link
Contributor

Hi @tmusbi, sorry for the late response! We've hit some delays with getting a new release out, but we hope to have this out by late October / early November. Thanks for your patience!

@hallvictoria
Copy link
Contributor

Posting a quick update here -- unfortunately we've hit some other delays in releasing, and we are slowing releases during the holiday season. Updated ETA is January. Sorry for the delays, and thanks again for your patience.

@santiagovasquez1
Copy link

any timeline for the next release?

@MarthinusBosman
Copy link

any timeline for the next release?

Try the comment right before yours.

@jreed-Aces
Copy link
Author

I see that the ticket is closed, but I don't see any comments about release. Has this been released, or is it still waiting?

@hallvictoria
Copy link
Contributor

@jreed-Aces, the release will start next week. The changes should be rolled out to all regions by end of February / early March and are included in runtime version 4.1037.

@jreed-Aces
Copy link
Author

@hallvictoria great, thank you for the update. I appreciate it

@TJKlein
Copy link

TJKlein commented Feb 9, 2025

run into the same issue using langchain Runnable method ainvoke. This worked for me but don't know why:

loop = asyncio.get_running_loop() loop.set_task_factory(None)

This also helped me

@robincolinet
Copy link

Hi @hallvictoria, thank you for your work and availability, I see that Function Apps runtime version 4.1037.0 has been released but I couldn't find an explicit mention for this bug there.
Pardon me if I missed it.

Can you please confirm it's included in the release, and migrating back to this runtime is safe?

In the meantime, +1 to @jreed-Aces 's suggested workaround. I deployed a new container with Python 3.10 2 months ago and it works like a charm. I'm just preoccupied about tech debt on this old runtime.

Thanks for your time and efforts!

@hallvictoria
Copy link
Contributor

Hi @robincolinet, yes, this fix is included in the 4.1037 train. 4.1037 is still being rolled out but will be available for all regions by the end of next week at the latest.

The runtime release notes will show updates to the Python worker version. You can view the changes for the Python worker in the releases page, and it will list what runtime version the changes will be a part of. Hope that helps!

@Gautamvaishnav-git
Copy link

Gautamvaishnav-git commented Mar 25, 2025

run into the same issue using langchain Runnable method ainvoke. This worked for me but don't know why:

loop = asyncio.get_running_loop() loop.set_task_factory(None)

This Worked for Me! 🚀

I was facing an issue where my Azure Function using LangChain's ainvoke() would only process the first request. After that, it would stop accepting new requests. I also saw this error:

TypeError: Dispatcher.dispatch_forever.<locals>.<lambda>() got an unexpected keyword argument 'context'

After some debugging, I realized that resetting the task factory multiple times was causing issues. Initially, I was doing:

loop = asyncio.get_running_loop()
loop.set_task_factory(None)

But this was being called on every request, which was breaking things.

How I Fixed It

I wrapped the task factory reset in a context manager, so it only resets during execution and restores the original factory after:

import asyncio

class ResetTaskFactory:
    def __enter__(self):
        self.loop = asyncio.get_running_loop()
        self.old_factory = self.loop.get_task_factory()
        self.loop.set_task_factory(None)

    def __exit__(self, exc_type, exc_value, traceback):
        self.loop.set_task_factory(self.old_factory)

# ✅ Usage inside Azure Function
async def main(req):
    with ResetTaskFactory():  # Ensures proper reset
        response = await tool.ainvoke("Hello!")
    return response

Why This Works?

✅ Prevents multiple resets that break async execution.
✅ Restores the original task factory after execution.
✅ Works properly with Azure Functions + LangChain/LangGraph.

Hope this helps someone! 🚀 Let me know if you have a better approach! 😊

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

Successfully merging a pull request may close this issue.