Phasukarn Sungkrasae
03/31/2026, 4:30 AMMarvin
03/31/2026, 4:30 AMMarvin
03/31/2026, 4:36 AMprefect deployment inspect "your-flow/your-deployment"
2) Ensure your worker/runtime is on a recent Prefect client (needed to renew leases)
- Use Prefect >= 3.4.11 on the worker image/venv running your flow.
3) Increase the grace period if infra startup is slow (Kubernetes/Docker can take >5–10 minutes)
- prefect.yaml:
deployments:
- name: your-deployment
concurrency_limit:
limit: 1
grace_period_seconds: 900 # e.g., 15 minutes
- Or via Python:
flow.from_source(...).deploy(
name="your-deployment",
concurrency_limit={"limit": 1, "grace_period_seconds": 900},
)
- As a server-wide fallback, you can raise PREFECT_SERVER_CONCURRENCY_INITIAL_DEPLOYMENT_LEASE_DURATION to match your environment, but per-deployment is usually best.
4) If you need a single limit across multiple deployments
- Deployment limits are per-deployment. To enforce a cross-deployment limit of 1, add a tiny “gate” task at the start of the flow with a shared tag and create a tag limit of 1:
# create once
prefect concurrency-limit create "flow-gate" --concurrency-limit 1
from prefect import flow, task
@task(tags=["flow-gate"])
def gate():
return
@flow
def my_flow(...):
gate() # ensures only one flow instance proceeds at a time
...
A few quick checks to help pinpoint the cause
- Are both overlapping runs from the same deployment?
- How long do runs sit in PENDING before starting? (>5 minutes often indicates lease expiry unless renewed)
- What Prefect version is installed in your worker/runtime image?
- Any logs about “concurrency lease” renewal or expiry around the time the second run started?
If you share those details, I can help confirm whether this is a lease-expiry issue and suggest the exact change needed.Phasukarn Sungkrasae
03/31/2026, 4:41 AMMarvin
03/31/2026, 4:41 AMMarvin
03/31/2026, 4:42 AMdeployments:
- name: your-deployment
concurrency_limit:
limit: 1
grace_period_seconds: 900 # e.g., 15 minutes
Or in code:
flow.from_source(...).deploy(
name="your-deployment",
concurrency_limit={"limit": 1, "grace_period_seconds": 900},
)
- You can also raise the server-wide default with PREFECT_SERVER_CONCURRENCY_INITIAL_DEPLOYMENT_LEASE_DURATION, but per-deployment is usually better.
3) Confirm it’s the same deployment
- Concurrency is per-deployment. If two different deployments exist, they won’t limit each other.
4) Optional: add logging to verify renewals
- Set PREFECT_LOGGING_LEVEL=DEBUG in the flow environment to see messages about acquiring/renewing the “deployment concurrency lease.” You’ll spot renew failures or expiry.
Quick checks to pinpoint
- What Prefect version is installed in the image/venv that runs the flow?
- Are both overlapping runs from the same deployment?
- What is your deployment’s grace_period_seconds (or the server default)?
- Any DEBUG logs about “maintaining” or “failed to renew” the concurrency lease?
If you share:
- the deployment’s concurrency config,
- your worker type (Kubernetes/Docker/Process) and Prefect version in the runtime,
- and whether the overlapping runs are from the same deployment,
I can confirm the exact culprit and give a targeted fix.Phasukarn Sungkrasae
03/31/2026, 4:49 AMMarvin
03/31/2026, 4:49 AMMarvin
03/31/2026, 4:51 AMconcurrency_limit in a place that only accepts an integer, which is why Pydantic says “Input should be a valid integer … input_type=dict”.
How to set it correctly
Pick the path you’re using:
- If you’re deploying in Python with Flow.deploy/to_deployment
Pass a ConcurrencyLimitConfig object (not a raw dict):
from prefect import flow
from prefect.client.schemas.objects import ConcurrencyLimitConfig, ConcurrencyLimitStrategy
@flow
def my_flow():
...
my_flow.deploy(
name="my-deployment",
work_pool_name="my-pool",
concurrency_limit=ConcurrencyLimitConfig(
limit=1,
grace_period_seconds=36000, # 10 hours, valid (60–86400)
collision_strategy=ConcurrencyLimitStrategy.ENQUEUE,
),
)
- If you’re using prefect.yaml with prefect deploy
A dict is valid here; the CLI will split it into the right fields:
deployments:
- name: my-deployment
work_pool:
name: my-pool
concurrency_limit:
limit: 1
grace_period_seconds: 36000
collision_strategy: enqueue # or cancel_new
- If you’re constructing RunnerDeployment directly
Set the int and options separately:
from prefect.deployments.runner import RunnerDeployment
from prefect.client.schemas.objects import ConcurrencyOptions, ConcurrencyLimitStrategy
deployment = RunnerDeployment(
name="my-deployment",
concurrency_limit=1,
concurrency_options=ConcurrencyOptions(
grace_period_seconds=36000,
collision_strategy=ConcurrencyLimitStrategy.ENQUEUE,
),
# ... other fields ...
)
Why this matters for your overlap issue
- Your earlier overlaps at ~4–15 minutes match a lease expiring. Increasing grace_period_seconds is the right fix, but you need to pass it via ConcurrencyLimitConfig (Python) or the YAML dict (CLI), not as a raw dict to concurrency_limit in Python.
Two final checks
- Ensure the runtime environment that executes the flow is Prefect >= 3.4.11 so it can renew the lease for the entire run.
- Confirm both overlapping runs are from the same deployment; limits are per-deployment.
If you share how you’re creating the deployment (Python vs prefect.yaml), I can tailor the exact snippet to your setup.