Tasks: a poll refreshes the routing keys and the snapshot, but not the stored arguments
Author: sclfczCreated Sep 17, 2026Updated Sep 17, 2026
Labelsbugserver
What happened?
A task's stored tool arguments are written once at creation and never extended, while the
other per-task keys are refreshed as the task is used. A task that outlives its arguments'
TTL and then re-enters for another leg gets {} from load_task_args() and re-runs the
tool with no arguments — silently, with no error and no log.
tests/tasks/server/test_task_ttl.py pins this sliding-refresh behavior for
_current_leg_key and for the context snapshot; the args key has no equivalent.
Example Code
Run from the repo root with uv run python repro.py:
import asyncio
from fastmcp import FastMCP
from fastmcp_tasks import TasksExtension
from fastmcp_tasks.input_store import _args_key, _current_leg_key
from tests.tasks.task_helpers import get_task, running_task_server, submit_task
async def main():
mcp = FastMCP("args-ttl")
mcp.add_extension(TasksExtension())
@mcp.tool(task=True)
async def slow_task() -> str:
await asyncio.Event().wait()
async with running_task_server(mcp):
created = await submit_task(mcp, "slow_task", {})
docket = mcp._docket
args_key = _args_key(docket, None, created.task_id)
leg_key = _current_leg_key(docket, None, created.task_id)
async with docket.redis() as redis:
await redis.expire(args_key, 5)
await redis.expire(leg_key, 5)
await get_task(mcp, created.task_id) # a poll
async with docket.redis() as redis:
print("args TTL after a poll:", await redis.ttl(args_key))
print("current_leg TTL after a poll:", await redis.ttl(leg_key))
asyncio.run(main())Output:
args TTL after a poll: 4
current_leg TTL after a poll: 1799Environment
fastmcp_tasks at main (490049f), Python 3.12, in-memory Docket backend.
Source: PrefectHQ/fastmcp