Taylor Bell
05/06/2026, 7:59 PMpause_flow_run is available in the SDK, but see no equivalent functionality in the REST API docs. We've tried using the set_state endpoint on a running job, but it just changes the state label and doesn't actually stop execution. Do you have a recommendation here?Taylor Bell
05/06/2026, 7:59 PMMarvin
05/06/2026, 7:59 PMMarvin
05/06/2026, 8:00 PMNate
05/06/2026, 8:00 PMNate
05/06/2026, 8:01 PMpause_flow_run in the SDK uses the APIMarvin
05/06/2026, 8:01 PMThang Le
05/06/2026, 8:02 PMThang Le
05/06/2026, 8:03 PMMarvin
05/06/2026, 8:03 PMpause_flow_run() is basically an SDK helper that proposes a PAUSED state to the API, then waits until the API transitions the run out of that paused state.
Here’s the mental model:
1. Your flow code calls pause_flow_run(...)
2. The SDK builds a Paused state object
3. The SDK sends that state to the API via the flow-run state orchestration endpoint
4. The API records/orchestrates the run into PAUSED
5. The SDK polls until the run is resumed
6. When resumed, execution continues, optionally with validated input
Important assumption: this is for Prefect 3.x.
---
## High-level call path
At a high level, the path is:
text
pause_flow_run()
-> creates a Paused state
-> propose_state(...)
-> client.set_flow_run_state(...)
-> POST /flow_runs/{id}/set_state
If wait_for_input=... is used, there is also flow-run input storage involved:
text
pause_flow_run(wait_for_input=MyModel)
-> save expected input schema/metadata
-> POST /flow_runs/{id}/input
-> set PAUSED state
-> later: POST /flow_runs/{id}/resume with input
-> API validates input against saved schema
-> stores response in flow run input
-> SDK loads that response and returns it
---
## What pause_flow_run() does in the SDK
Internally, pause_flow_run() / apause_flow_run() creates a Paused state with details like:
- timeout_seconds
- pause_key
- whether it should reschedule
- optional run_input_keyset metadata if input is expected
Conceptually, it looks like:
text
Paused(
timeout_seconds=...,
pause_key=...,
reschedule=False
)
This produces a state whose type is PAUSED, with important values stored in state_details.
Those details include things like:
- pause_key
- pause_timeout
- pause_reschedule
- run_input_keyset if wait_for_input is set
The pause_key is especially important because it gives the pause a stable identity, which helps prevent the same pause point from being treated ambiguously.
---
## How it uses the API to pause the run
Once the SDK has built the Paused state, it sends it through the normal state orchestration mechanism.
That goes through:
- propose_state() / propose_state_sync()
- then client.set_flow_run_state(...)
Which results in an API call to:
text
POST /flow_runs/{id}/set_state
with a payload conceptually like:
json
{
"state": {
"type": "PAUSED",
"state_details": {
"pause_key": "...",
"pause_timeout": "...",
"pause_reschedule": false
}
},
"force": false
}
The server endpoint then runs orchestration logic and returns an OrchestrationResult indicating whether the proposed state transition was accepted, rejected, aborted, or delayed.
So the key point is:
pause_flow_run() does not directly “flip a DB flag” — it uses the same orchestrated state-transition API as other flow-run state changes.
---
## How the paused state is represented
At the API/model level, the run is paused by putting the flow run into a PAUSED state.
That state includes metadata in state_details, especially:
- `pause_key`: identifies the pause instance
- `pause_timeout`: when the pause should expire
- `pause_reschedule`: whether resume should go to RUNNING or to a scheduled/resuming path
- `run_input_keyset`: where input schema/description/response are stored
So the “signal” that the run is paused is literally:
- the flow run’s current state type is PAUSED
- and the pause-specific metadata lives in state_details
---
## What happens if wait_for_input is used
This is the most interesting part.
If you call something like:
python
user_input = pause_flow_run(wait_for_input=MyInputModel)
the SDK needs to tell the API what kind of input is expected later.
It does that by creating a flow run input keyset and storing schema-related records using the flow-run input API.
That uses:
text
POST /flow_runs/{id}/input
The stored records include things like:
- schemaMarvin
05/06/2026, 8:03 PM...-schema
- ...-description
- ...-response
So before the run is paused, the SDK persists enough metadata for the API to validate the eventual resume payload.
### Why this matters
This means the API can enforce that a resume request provides input matching the expected schema, instead of blindly accepting arbitrary JSON.
---
## What the SDK does while the run is paused
After the API accepts the PAUSED state, the SDK does not “return immediately and forget everything”.
Instead, it waits by polling the flow run state periodically.
Conceptually:
text
while flow run state is PAUSED:
sleep(poll_interval)
fetch current flow run state
When the run leaves the paused state, the SDK continues.
If wait_for_input was used, it then retrieves the stored response and returns it from pause_flow_run().
So from the flow author’s perspective, it behaves like a blocking pause point:
python
approval = pause_flow_run(wait_for_input=Approval)
# execution resumes here after API-side resume
---
## How resume works at the API level
Resume happens through a dedicated endpoint:
text
POST /flow_runs/{id}/resume
This is separate from set_state.
The server does roughly this:
1. Confirms the flow run is currently paused
2. If input is supplied, finds the saved schema via the run_input_keyset
3. Validates the provided input against that schema
4. Stores the validated response in flow run input storage
5. Transitions the run out of PAUSED
If pause_reschedule=False (normal pause), resume transitions the run to something like:
- Running()
If pause_reschedule=True (more like suspend/reschedule behavior), resume may transition it through:
- Scheduled(name="Resuming")
That distinction matters because some pauses are meant to continue in-place, while others are intended to be rescheduled onto infrastructure.
---
## Resume with input: validation flow
When input is passed to the resume endpoint, the API does schema-driven validation.
The pattern is:
1. Read saved schema from flow-run input storage
2. Validate submitted run_input against schema
3. Reject the resume if validation fails
4. Save the validated response under the response key if validation succeeds
So the API is not trusting the client to validate correctly; the validation happens server-side too.
That’s a strong design choice because it keeps the pause/resume contract authoritative at the API layer.
---
## Endpoints involved
The main API endpoints involved are:
### Pause via state orchestration
text
POST /flow_runs/{id}/set_state
Used to propose the PAUSED state.
### Flow run input storage
text
POST /flow_runs/{id}/input
Used to store:
- schema
- description
- response
### Resume endpoint
text
POST /flow_runs/{id}/resume
Used to:
- validate any provided input
- store the response
- transition the flow run out of PAUSED
---
## Relevant models/concepts
The important pieces are:
- Paused state
The SDK-side state object used to represent a pause request.
- StateDetails
Holds pause metadata like:
- pause_key
- pause_timeout
- pause_reschedule
- run_input_keyset
- OrchestrationResult
Returned by the state orchestration endpoint to indicate whether the transition was accepted.
- Flow run input records
Used to store expected schema and actual resumed input.
---
## Practical summary
In one sentence:
pause_flow_run() uses the API by proposing a PAUSED state through /flow_runs/{id}/set_state, optionally persisting input schema via /flow_runs/{id}/input, then waiting until /flow_runs/{id}/resume transitions the run back out of pause.
Or even shorter:
- Pause = orchestrated state transition to PAUSED
- Input contract = stored as flow-run input recordsMarvin
05/06/2026, 8:03 PMpause_flow_run() is not just a local Python control-flow primitive. It’s really an orchestrated API-backed checkpoint.
That’s why it can support:
- UI/API-driven resumes
- validated human input
- timeout handling
- stateful pause identity via pause_key
---
If you want, I can also give you:
- a sequence diagram of the pause/resume API calls, or
- a minimal code example showing pause_flow_run(wait_for_input=...) and what the corresponding API interactions look like.Thang Le
05/06/2026, 8:03 PMThang Le
05/06/2026, 8:04 PMThang Le
05/06/2026, 8:38 PMMarvin
05/06/2026, 8:38 PMMarvin
05/06/2026, 8:40 PMpause_flow_run() does exist and works in Prefect 3.0.11.
Based on the code and API for that version, 3.0.11 includes:
- pause_flow_run
- resume_flow_run
- POST /flow_runs/{id}/set_state
- POST /flow_runs/{id}/resume
- flow-run input support for wait_for_input=...
So the overall pause/resume mechanism I described does apply to 3.0.11.
## What is supported in 3.0.11
In 3.0.11, pause_flow_run() can:
- pause a flow run by proposing a PAUSED state
- optionally wait for typed input with wait_for_input=...
- resume via the API/UI
- validate resume input against stored schema
- continue execution after resume
## Important caveats for 3.0.11
A few version-specific notes:
- Default timeout is 1 hour (3600 seconds), unless you override it.
- There is a distinction between blocking pause and rescheduling/suspend-like behavior.
- In 3.0.11, this functionality is implemented with the older @sync_compatible pattern internally; that’s fine, just an internal implementation detail.
- One notable limitation in 3.0.11: pausing from within a task was not yet fixed there.
That last point matters a lot:
### In 3.0.11, pausing from a task is a problem
A later fix changed pause_flow_run() so it could pause the parent flow when called inside a task.
That fix came later, not in 3.0.11.
So in 3.0.11:
- calling pause_flow_run() inside a flow → yes, expected to work
- calling pause_flow_run() inside a task → likely not supported / may raise an error like Cannot pause task runs
## Bottom line
Yes, it works in 3.0.11 if you mean the standard flow pause/resume behavior.
But if your use case is:
- pausing from inside a task
- or relying on newer UX fixes around pause input forms
then 3.0.11 may be too old.
## Useful links
- Prefect repo: Prefect on GitHub
- Later fix for pause-from-task behavior: PR #19457
- Example related issue/bug around pause input UX in newer versions: Issue #21491
If you want, I can also answer this more concretely for your exact usage:
- pause inside a flow
- pause inside a task
- pause with wait_for_input
- resume via UI vs APIBring your towel and join one of the fastest growing data communities. Welcome to our second-generation open source orchestration platform, a completely rethought approach to dataflow automation.
Powered by