Return 409 on workflow updates race conditions (#4510)

This commit is contained in:
Stanislav Novosad
2026-01-21 15:45:52 -07:00
committed by GitHub
parent f879f4983c
commit f781a6f0ef
2 changed files with 38 additions and 24 deletions

View File

@@ -54,6 +54,14 @@ class FailedToUpdateWorkflow(BaseWorkflowHTTPException):
) )
class WorkflowVersionConflict(BaseWorkflowHTTPException):
def __init__(self, workflow_permanent_id: str) -> None:
super().__init__(
f"Concurrent update detected for workflow {workflow_permanent_id}. Please retry.",
status_code=status.HTTP_409_CONFLICT,
)
class OutputParameterKeyCollisionError(BaseWorkflowHTTPException): class OutputParameterKeyCollisionError(BaseWorkflowHTTPException):
def __init__(self, key: str, retry_count: int | None = None) -> None: def __init__(self, key: str, retry_count: int | None = None) -> None:
message = f"Output parameter key {key} already exists in the context manager." message = f"Output parameter key {key} already exists in the context manager."

View File

@@ -60,6 +60,7 @@ from skyvern.forge.sdk.schemas.workflow_runs import WorkflowRunBlock, WorkflowRu
from skyvern.forge.sdk.trace import TraceManager from skyvern.forge.sdk.trace import TraceManager
from skyvern.forge.sdk.workflow.exceptions import ( from skyvern.forge.sdk.workflow.exceptions import (
InvalidWorkflowDefinition, InvalidWorkflowDefinition,
WorkflowVersionConflict,
) )
from skyvern.forge.sdk.workflow.models.block import ( from skyvern.forge.sdk.workflow.models.block import (
BlockTypeVar, BlockTypeVar,
@@ -1586,30 +1587,35 @@ class WorkflowService:
sequential_key: str | None = None, sequential_key: str | None = None,
folder_id: str | None = None, folder_id: str | None = None,
) -> Workflow: ) -> Workflow:
return await app.DATABASE.create_workflow( try:
title=title, return await app.DATABASE.create_workflow(
workflow_definition=workflow_definition.model_dump(), title=title,
organization_id=organization_id, workflow_definition=workflow_definition.model_dump(),
description=description, organization_id=organization_id,
proxy_location=proxy_location, description=description,
webhook_callback_url=webhook_callback_url, proxy_location=proxy_location,
max_screenshot_scrolling_times=max_screenshot_scrolling_times, webhook_callback_url=webhook_callback_url,
totp_verification_url=totp_verification_url, max_screenshot_scrolling_times=max_screenshot_scrolling_times,
totp_identifier=totp_identifier, totp_verification_url=totp_verification_url,
persist_browser_session=persist_browser_session, totp_identifier=totp_identifier,
model=model, persist_browser_session=persist_browser_session,
workflow_permanent_id=workflow_permanent_id, model=model,
version=version, workflow_permanent_id=workflow_permanent_id,
is_saved_task=is_saved_task, version=version,
status=status, is_saved_task=is_saved_task,
extra_http_headers=extra_http_headers, status=status,
run_with=run_with, extra_http_headers=extra_http_headers,
cache_key=cache_key, run_with=run_with,
ai_fallback=False if ai_fallback is None else ai_fallback, cache_key=cache_key,
run_sequentially=run_sequentially, ai_fallback=False if ai_fallback is None else ai_fallback,
sequential_key=sequential_key, run_sequentially=run_sequentially,
folder_id=folder_id, sequential_key=sequential_key,
) folder_id=folder_id,
)
except IntegrityError as e:
if "uc_org_permanent_id_version" in str(e) and workflow_permanent_id:
raise WorkflowVersionConflict(workflow_permanent_id) from e
raise
async def create_workflow_from_prompt( async def create_workflow_from_prompt(
self, self,