Jesse
03/11/2026, 9:27 AMMarvin
03/11/2026, 9:27 AMMarvin
03/11/2026, 9:30 AM@task(tags=["db","prod"])) and your workspace has a concurrency limit for any of those tags, Prefect will acquire a lease for each matching tag while that task runs. If the lease can’t be renewed (expired, revoked, limit deleted/reset, network hiccup), you’ll see this error and the task/run will be terminated to avoid over-allocation.
- A global limit was used in code: If you (or a library you use) wrapped code with prefect.concurrency.concurrency(...), that also acquires/renews a lease. Renewal failures will terminate execution.
- The limit was changed mid-run: Deleting/resetting a limit while there are active leases will cause renewals to fail for those runs.
- Transient issues: Loss of connectivity to Prefect (Cloud or server) or worker/container restarts can cause renewal to fail.
How to confirm quickly
- List existing tag concurrency limits:
prefect concurrency-limit ls
- Inspect a specific limit to see active slots (which task runs are holding leases):
prefect concurrency-limit inspect <limit_name>
- Check your code for:
- Task or flow tags (@task(tags=[...]), with tags(...):)
- Any usage of from prefect.concurrency import concurrency (or .sync/.async variants)
- Turn on debug logs for more detail: set PREFECT_LOGGING_LEVEL=DEBUG and re-run to see which limit name was involved.
Common fixes
- If a tag limit is unintentionally catching your tasks, remove the limit or rename/remove the tag on those tasks.
- Avoid deleting/resetting limits while runs are active; disable them instead, then drain.
- For manual/global limits, increase lease_duration to exceed your longest critical section and avoid tight renew windows.
- If you don’t want execution to stop on renewal failure for manual limits, ensure strict=False when using concurrency(...).
A couple of clarifying questions to help pinpoint the cause:
- Do any of your tasks or flows have tags like “prod”, “db”, “api”, etc.?
- Does prefect concurrency-limit ls show any active limits that might match those tags?
- Did anyone recently delete or reset a concurrency limit while runs were in progress?
- Are you using prefect.concurrency.concurrency(...) anywhere in your code?
If you can share a flow run ID or a snippet of the run logs around the error, I can help identify the exact limit/name that triggered the termination.Jesse
03/11/2026, 10:47 AMprefect concurrency-limit ls related to these flows/tasks, the tasks have no tags, there’s no import concurrency anywhere.Marvin
03/11/2026, 10:47 AMMarvin
03/11/2026, 10:50 AMprefect concurrency-limit ls only shows task/tag-based limits. It does not show deployment-level concurrency.
- No task tags and no concurrency(...) usage rules out task/global limits, but a deployment with concurrency enabled will still create and renew a lease for each flow run.
How to verify
- Check the deployment for a concurrency setting (UI is the clearest: Deployment > Settings > Concurrency).
- Or via CLI:
prefect deployment inspect "<flow_name>/<deployment_name>"
Look for a concurrency-related field (e.g., a concurrency section/options) in the JSON.
Common reasons leases fail even when you didn’t intend to use limits
- Deployment concurrency is/was configured (possibly set earlier) and a run still holds a lease.
- The server restarted while using in-memory lease storage (OSS default), which drops leases; renewals then fail.
- The concurrency setting was changed/removed while runs were active.
- Transient network/API issues caused the renewal retries to exhaust.
Next steps
- If you don’t want deployment-level concurrency, remove or disable it on the deployment and redeploy.
- If you do want it:
- Avoid deleting/changing it while runs are active.
- If you’re on OSS server, consider persistent lease storage to survive restarts:
PREFECT_SERVER_CONCURRENCY_LEASE_STORAGE=prefect.server.concurrency.lease_storage.filesystem
- If you suspect transient issues, re-run with debug logs to see which lease is involved:
PREFECT_LOGGING_LEVEL=DEBUG
If you share a flow run ID and the deployment name, I can confirm whether a deployment concurrency lease was attached and why the renewal failed. Are you running in Prefect Cloud or a self-hosted server, and did the server/agent restart around the time of these cancellations?Jesse
03/11/2026, 10:56 AM069b0361-3271-73ae-8000-3591a504f409
'global_concurrency_limit': {
'id': '96796d28-609d-4cc0-ac4f-aff45294f93b',
'created': '2026-03-09T10:13:21.213981Z',
'updated': '2026-03-11T10:52:39.639719Z',
'active': True,
'name': 'deployment:656afbd5-05bb-4623-a03c-e8247a68f114',
'limit': 1,
'active_slots': 1,
'slot_decay_per_second': 0.0
},
'concurrency_options': {'collision_strategy': 'ENQUEUE', 'grace_period_seconds': 900},
How do I delete this?Marvin
03/11/2026, 10:56 AMMarvin
03/11/2026, 11:00 AMfrom prefect import flow
@flow
def my_flow():
...
my_flow.deploy(
name="your-deployment",
work_pool_name="your-pool",
concurrency_limit=None, # remove deployment concurrency
)
- If you use prefect.yaml, ensure the deployment entry does not set concurrency_limit (or set it to null), then run:
prefect deploy
Quick CLI actions against the existing limit
- Disable it now (stops enforcement without deleting the record; safer for active runs):
prefect global-concurrency-limit disable "deployment:656afbd5-05bb-4623-a03c-e8247a68f114"
- Delete it (removes the record immediately):
prefect global-concurrency-limit delete "deployment:656afbd5-05bb-4623-a03c-e8247a68f114"
Note: Deleting while runs are active will cause their lease renewals to fail (the exact error you saw) and those runs may be canceled. If you need to avoid that, disable first, let runs finish, then redeploy with concurrency_limit=None so it doesn’t get recreated.
Verify
- List all global limits:
prefect global-concurrency-limit ls
- Inspect that specific limit:
prefect global-concurrency-limit inspect "deployment:656afbd5-05bb-4623-a03c-e8247a68f114"
- After removal, confirm it no longer appears and that your deployment no longer shows a concurrency setting (UI or prefect deployment inspect "<flow>/<deployment>").
If you want, share the deployment name and I can suggest the exact redeploy command/snippet to remove it cleanly.Jesse
03/11/2026, 11:00 AMMarvin
03/11/2026, 11:00 AMMarvin
03/11/2026, 11:02 AMmy_flow.deploy(
name="your-deployment",
work_pool_name="your-pool",
concurrency_limit=None, # explicitly clear
)
Or in prefect.yaml set:
concurrency_limit: null
then run:
prefect deploy
- If you need to act immediately on an existing limit:
- Disable enforcement:
prefect global-concurrency-limit disable "deployment:656afbd5-05bb-4623-a03c-e8247a68f114"
- Or delete it (as you did):
prefect global-concurrency-limit delete "deployment:656afbd5-05bb-4623-a03c-e8247a68f114"
Tip: After you clear it via redeploy, verify it’s gone and won’t be recreated:
- Check the deployment detail (UI or)
prefect deployment inspect "<flow>/<deployment>"
- List global limits:
prefect global-concurrency-limit ls
If you still see it return after a redeploy with concurrency_limit=None, let me know the deployment name and how you’re deploying (Flow.deploy, from_source().deploy, or prefect.yaml), and I’ll help trace the exact payload being sent.Jesse
03/11/2026, 4:17 PM'concurrency_limit': None,
'global_concurrency_limit': None,
It still crashed with Concurrency lease renewal failed - slots are no longer reserved. Terminating execution to prevent over-allocation.".Marvin
03/11/2026, 4:17 PMMarvin
03/11/2026, 4:20 PMprefect global-concurrency-limit disable "deployment:<deployment_id>"
Then redeploy with concurrency_limit=None so new runs won’t acquire a lease. Let the old runs finish (their lease will still renew because the lease object still exists), then you can delete the limit if you want.
- Important: Omitting concurrency_limit on redeploy leaves the old setting in place; you must explicitly set concurrency_limit=None to remove it.
Useful checks you can run in your workspace
- Verify the run’s details (you’ll need to run this in your own environment):
prefect flow-run inspect 069b0361-3271-73ae-8000-3591a504f409
Look for state_details.deployment_concurrency_lease_id. If present, that confirms this was a carry-over lease.
- List global limits to ensure nothing reappeared:
prefect global-concurrency-limit ls
Can you confirm:
- Whether this failing run was created before you redeployed with concurrency_limit=None?
- Whether new runs (created after the redeploy) still show this error?
- In the failing run’s JSON, does state_details include deployment_concurrency_lease_id?
If a brand-new run (post-redeploy) still fails, share the run’s deployment name and I’ll help double-check the deployment config and the exact cleanup steps to ensure no hidden limit is being recreated.Jesse
03/11/2026, 4:22 PMHeartbeat loop cancelled
Stopped flow run heartbeat context
This run was after deleting the global concurrency limit, so still showing the error.Marvin
03/11/2026, 4:22 PMMarvin
03/11/2026, 4:24 PMprefect flow-run inspect 069b0361-3271-73ae-8000-3591a504f409
Look for state_details.deployment_concurrency_lease_id. If it’s present, that’s why the maintainer ran and cancelled the run when renewal failed.
2) Verify the deployment that created this run truly has no concurrency:
- In UI: Deployment > Settings > Concurrency should show “Not limited”
- Or:
prefect deployment inspect "<flow_name>/<deployment_name>"
Confirm it does not show a concurrency section and that global_concurrency_limit is null.
3) Double-check there isn’t another deployment being used:
- In the run details (UI), click the Deployment link and verify its ID matches the one you updated.
- It’s easy to have two deployments with the same name in different projects/workspaces or created by different pipelines.
4) Ensure no global limit got recreated:
prefect global-concurrency-limit ls
Confirm there is no entry named deployment:<deployment_id>.
Why this can still happen “after deletion”
- If the run was created (or moved to Running) while the deployment still had a concurrency limit, it already holds a lease. Deleting the limit afterward makes the next renewal return 410; the engine cancels with the exact message you saw.
- If the deployment still carried a concurrency limit (because it was omitted on redeploy), the orchestrator will acquire a new lease for new runs, even if you previously deleted the limit record.
Safe cleanup pattern
- If you need existing in-flight runs to finish, don’t delete the limit record immediately. First disable it:
prefect global-concurrency-limit disable "deployment:<deployment_id>"
Then redeploy with concurrency_limit=None so new runs won’t acquire a lease. Let old runs finish (their lease can still renew). Finally, delete the limit if you want it gone.
- If you already deleted it and a run is still failing, cancel that run and trigger a fresh one created after the redeploy that explicitly set concurrency_limit=None.
ECS logging note
- The concurrency-maintainer logs at error on failure, so setting PREFECT_LOGGING_LEVEL=DEBUG won’t add many extra lines around this path—what you saw (heartbeat loop cancelled) is typical when the engine shuts down.
- Make sure the env var is applied to the ECS task’s container (deployment job variables is the right place).
If you can paste the output of:
- prefect flow-run inspect 069b0361-3271-73ae-8000-3591a504f409 (state_details section)
- prefect deployment inspect "<flow>/<deployment>" (concurrency fields)
I’ll call out exactly where the lease is coming from and the next exact step to clear it.