<@ULVA73B9P> I have a flow parameter that is a uni...
# ask-marvin
s
@Marvin I have a flow parameter that is a union datatype of date and enum. When I run a deployment on the UI and look at this parameter, I see a date and MyEnum. The date picker works. When I flip to MyEnum, I see a dropdown. If I select a value, I am moved back to the date selection. When I navigate back to the MyEnum option, it appears that no value is selected. However, when I look at the JSON input, it is indeed selected. Is there anything I can do to get this type to show up properly here?
m
thought for 200.4 seconds
Thanks for the clear description — that sounds like a UI rendering quirk with Union parameters made of “primitive” types (date + enum). The fact that the JSON input shows the correct enum selection but the control flips back to the date tab suggests the backend/schema and value are fine, but the UI isn’t retaining the selected variant’s visible state. A couple of quick questions to help us narrow it down: - Are you on Prefect 3.x (Core) and using Prefect Cloud or self-hosted Prefect Server? - If self-hosting, which UI version are you running? - Could you share a minimal example of your parameter type (enum definition + flow signature)? In the meantime, here are reliable patterns that avoid this UI edge case: Workaround A: Use a discriminated union of models - Instead of a union of primitives, wrap each option in a Pydantic model and add a discriminator. The UI tends to handle these unions more consistently.
Copy code
from datetime import date
from enum import Enum
from typing import Union, Annotated
from pydantic import BaseModel, Field
from typing_extensions import Literal
from prefect import flow

class MyEnum(str, Enum):
    A = "A"
    B = "B"

class DateChoice(BaseModel):
    kind: Literal["date"]
    value: date

class EnumChoice(BaseModel):
    kind: Literal["enum"]
    value: MyEnum

Param = Annotated[Union[DateChoice, EnumChoice], Field(discriminator="kind")]

@flow
def my_flow(x: Param):
    print(x)
Workaround B: One model with two optional fields + validation - Show both inputs and validate that exactly one was provided.
Copy code
from datetime import date
from enum import Enum
from pydantic import BaseModel, model_validator
from prefect import flow

class MyEnum(str, Enum):
    A = "A"
    B = "B"

class Input(BaseModel):
    enum_value: MyEnum | None = None
    run_date: date | None = None

    @model_validator(mode="after")
    def _one_of(self):
        if (self.enum_value is None) == (self.run_date is None):
            raise ValueError("Provide exactly one of enum_value or run_date")
        return self

@flow
def my_flow(x: Input):
    print(x)
Workaround C: Two separate parameters with runtime validation - Keep
run_date: date | None
and
enum_value: MyEnum | None
as separate params and assert that exactly one is set at the start of the flow. A couple of additional tips: - Setting a default value for the parameter (e.g., defaulting to the enum variant) can “anchor” the initially selected tab in some cases, though it may not fix the tab-flipping you’re seeing. - If you’re self-hosting, consider upgrading the UI to the latest version in case this has already been addressed. I wasn’t able to find an existing GitHub issue for this specific behavior in the UI. If you can share a minimal reproducer (your enum, flow signature, and how you deploy), I’m happy to file a bug in the UI repo on your behalf, or you can open one directly here: - Prefect UI repo: PrefectHQ/prefect-ui - Prefect Core repo (for reference): PrefectHQ/prefect If you prefer, drop a minimal code snippet here and your environment details, and I’ll package it up for the team.
s
@Marvin I am using prefect Prefect 3 and self hosting. My UI version is 3.4.25. My type looks like
Copy code
class MyEnum(Enum):
    VALUE_ONE = "VALUE_ONE"
    ...


MyUnionType = Union[date, MyEnum]
And a flow looks like
Copy code
@flow
def my_flow(input: MyUnionType = MyEnum.VALUE_ONE):
    ...
m
thought for 45.9 seconds
Thanks for the details — that helps. Given you’re on Prefect 3 and self-hosting the UI 3.4.25, what you’re seeing is consistent with a UI quirk when rendering Union parameters made of “primitive” types (date + enum). The backend is fine (as you noticed from the JSON), but the UI isn’t retaining the selected variant’s visible state. Quick things to try - Reorder the union so the enum comes first. Sometimes the UI anchors to the first variant:
Copy code
MyUnionType = Union[MyEnum, date]
# or with | syntax: MyUnionType = MyEnum | date
You already default to
MyEnum.VALUE_ONE
, so this change may help the UI default/rendering heuristics. More reliable workarounds If the above doesn’t stick, these patterns are much more stable in the UI: 1) Discriminated union of models (recommended) - Wrap each variant in a small model and add a discriminator; the UI tends to handle this well.
Copy code
from datetime import date
from enum import Enum
from typing import Union, Annotated
from typing_extensions import Literal
from pydantic import BaseModel, Field
from prefect import flow

class MyEnum(str, Enum):
    VALUE_ONE = "VALUE_ONE"
    # ...

class DateChoice(BaseModel):
    kind: Literal["date"]
    value: date

class EnumChoice(BaseModel):
    kind: Literal["enum"]
    value: MyEnum

MyUnionType = Annotated[Union[DateChoice, EnumChoice], Field(discriminator="kind")]

@flow
def my_flow(input: MyUnionType = EnumChoice(kind="enum", value=MyEnum.VALUE_ONE)):
    ...
2) Single model with two optional fields + validation - Show both inputs and enforce that exactly one is provided.
Copy code
from datetime import date
from enum import Enum
from pydantic import BaseModel, model_validator
from prefect import flow

class MyEnum(str, Enum):
    VALUE_ONE = "VALUE_ONE"
    # ...

class Input(BaseModel):
    enum_value: MyEnum | None = MyEnum.VALUE_ONE
    run_date: date | None = None

    @model_validator(mode="after")
    def _one_of(self):
        if (self.enum_value is None) == (self.run_date is None):
            raise ValueError("Provide exactly one of enum_value or run_date")
        return self

@flow
def my_flow(input: Input = Input()):
    ...
Upgrade suggestion - If you can, try upgrading the UI to the latest available version; there have been periodic fixes to form rendering. Since you’re self-hosted, updating just the UI container/image may be sufficient depending on your deployment. - Prefect UI repo: PrefectHQ/prefect-ui - Form building docs (helpful for shaping parameters the UI handles best): Form building in Prefect 3 If reordering the union doesn’t help and you’d prefer not to change the type, I can open a UI issue with your minimal example. If you’re up for it, please also share: - Browser and version - How you deploy the UI (image tag/Helm chart version) - A short GIF or screenshots showing the selection flipping I’m happy to file the bug on your behalf with that info.