diff --git a/src/prefect/server/api/clients.py b/src/prefect/server/api/clients.py index 2e94e0bbd83e..7c1d67ea7d0d 100644 --- a/src/prefect/server/api/clients.py +++ b/src/prefect/server/api/clients.py @@ -31,13 +31,13 @@ class BaseClient: _http_client: PrefectHttpxAsyncClient def __init__(self, additional_headers: dict[str, str] | None = None): - from prefect.server.api.server import create_app + from prefect.server.api.server import create_api_app additional_headers = additional_headers or {} - # create_app caches application instances, and invoking it with no arguments - # will point it to the the currently running server instance - api_app = create_app() + # create_api_app creates only the API routes without UI or background + # services, which is all OrchestrationClient needs for in-process calls. + api_app = create_api_app() settings = get_current_settings() diff --git a/src/prefect/server/events/actions.py b/src/prefect/server/events/actions.py index afecf80b02d0..7e63160dac90 100644 --- a/src/prefect/server/events/actions.py +++ b/src/prefect/server/events/actions.py @@ -1811,6 +1811,13 @@ async def message_handler(message: Message): except ActionFailed as e: # ActionFailed errors are expected errors and will not be retried await action.fail(triggered_action, e.reason) + except Exception: + logger.exception( + "Unexpected error executing action %s for automation %s", + triggered_action.id, + triggered_action.automation.id, + ) + raise else: await action.succeed(triggered_action) await record_action_happening(triggered_action.id) diff --git a/tests/server/api/test_clients.py b/tests/server/api/test_clients.py index f068e9a29a1b..db3ad902672e 100644 --- a/tests/server/api/test_clients.py +++ b/tests/server/api/test_clients.py @@ -12,7 +12,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from prefect.server.api.clients import OrchestrationClient -from prefect.server.api.server import create_app +from prefect.server.api.server import create_api_app, create_app from prefect.server.models import deployments, flow_runs, flows from prefect.server.models.variables import create_variable from prefect.server.schemas.actions import VariableCreate @@ -260,6 +260,20 @@ async def test_read_variables_with_error(orchestration_client: OrchestrationClie await orchestration_client.read_workspace_variables() +async def test_orchestration_client_uses_api_app(): + """ + Regression test for https://github.com/PrefectHQ/prefect/issues/19317 + + OrchestrationClient should use create_api_app instead of create_app to avoid + UI static file creation and background services that fail in read-only containers. + """ + with mock.patch( + "prefect.server.api.server.create_api_app", wraps=create_api_app + ) as mock_create_api_app: + OrchestrationClient() + mock_create_api_app.assert_called_once() + + async def test_get_orchestration_client_after_create_app_final(): """ Regression test for https://github.com/PrefectHQ/prefect/issues/17451