Documentation Index
Fetch the complete documentation index at: https://polos.dev/docs/llms.txt
Use this file to discover all available pages before exploring further.
Stop workflows to prevent runaway executions and free up resources. Workflows can be cancelled via timeout or manual cancellation.
Timeout-based cancellation
Set run_timeout_seconds to automatically cancel workflows that run too long:
from polos import workflow, WorkflowContext, PolosClient
@workflow
async def risky_workflow(ctx: WorkflowContext, input: dict):
# Potentially long-running workflow
# Invoke with timeout
client = PolosClient()
handle = await risky_workflow.invoke(
client,
payload={"data": "..."},
run_timeout_seconds=1800 # Cancel after 30 minutes
)
How timeouts work:
- Only applies to running workflows (actively executing)
- Does not apply to waiting workflows (paused on
wait_for, wait_for_event, etc.)
- Orchestrator and/or worker automatically cancels execution when timeout is reached
- Workflow stops immediately
Example with waits:
@workflow
async def workflow_with_waits(ctx: WorkflowContext, input: dict):
# Step 1: Process (counts toward timeout)
await ctx.step.run("process", process_data, input)
# Step 2: Wait 1 hour (does NOT count toward timeout)
await ctx.step.wait_for("wait", hours=1)
# Step 3: More processing (counts toward timeout, but resets execution time of step 1)
await ctx.step.run("finalize", finalize_data, input)
# Timeout only counts running time, not waiting time
client = PolosClient()
handle = await workflow_with_waits.invoke(
client,
payload={...},
run_timeout_seconds=60 # 60 seconds of continuous running time
)
Run timeout vs total duration:
run_timeout_seconds=60 → Workflow can run for 60 seconds of active execution
- If workflow waits for 24 hours, that time does not count toward timeout
- Total workflow duration can exceed
run_timeout_seconds if it includes waits
Manual cancellation
Cancel workflows manually using the SDK or API
from polos import PolosClient
client = PolosClient()
# Start workflow
handle = await risky_workflow.invoke(client, {"data": "..."})
# Cancel manually
cancelled = await client.cancel_execution(handle.id)
if cancelled:
print("Workflow cancelled successfully")
else:
print("Workflow already completed or not found")
Or via the handle:
handle = await risky_workflow.invoke(client, {"data": "..."})
# Cancel using handle
cancelled = await handle.cancel(client)
Or via API:
import httpx
async def cancel_workflow_api(execution_id: str, api_key: str):
"""Cancel workflow via API."""
async with httpx.AsyncClient() as client:
response = await client.post(
f"https://api.polos.ai/api/v1/executions/{execution_id}/cancel",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
)
if response.status_code == 200:
return True
elif response.status_code == 404:
print(f"Execution not found: {execution_id}")
return False
else:
response.raise_for_status()
What can be cancelled
- ✅ Running, queued or waiting workflows can be cancelled
- ❌ Completed or failed workflows cannot be cancelled
How does it work?
When a workflow is cancelled:
- Orchestrator issues cancel signal to the worker
- Worker stops execution immediately
- Workflow status changes to “cancelled”
- No more steps execute
Current step behavior:
- If a step is running when cancel is issued, it may complete before stopping
- Steps are not interrupted mid-execution
- Cancellation takes effect between steps