Skip to content

branch_create fails validation: OperationAction missing 'epc_sync' #21

Description

@ZainRizvi

branch_create now fails validation because Neon returns the epc_sync operation action, which OperationAction does not contain. Any call whose response includes that action raises:

pydantic_core._pydantic_core.ValidationError: 1 validation error for BranchOperations
operations.3.action
  Input should be 'create_compute', 'create_timeline', 'start_compute', 'suspend_compute', 'apply_config', 'check_availability', 'delete_timeline', 'create_branch', 'tenant_ignore', 'tenant_attach', 'tenant_detach', 'tenant_reattach', 'replace_safekeeper', 'disable_maintenance', 'apply_storage_config', 'prepare_secondary_pageserver', 'switch_pageserver', 'detach_parent_branch', 'timeline_archive', 'timeline_unarchive', 'start_reserved_compute', 'sync_dbs_and_roles_from_compute' or 'timeline_update_protected_config' [type=enum, input_value='epc_sync', input_type=str]

The traceback ends inside the client's own validation decorator:

neon_api/client.py:39: in wrapper
    return model(**func(*args, **kwargs))

Reproducing

Any branch_create against a project where the response includes an epc_sync operation. In our case it is the 4th entry in operations, so the failure is intermittent in the sense that it depends on which operations Neon attaches to a given branch creation.

from neon_api import NeonAPI

neon = NeonAPI(api_key=...)
neon.branch_create(project_id=..., branch={...}, endpoints=[{"type": "read_write"}])
# ValidationError as above

Version: neon-api==0.3.0 (current release), pydantic==2.13.

Cause

epc_sync was added to OperationAction in the OpenAPI spec refresh in neondatabase/neon-pkgs#330 (merged 2026-07-25), which shipped in the JS/TS SDK (@neon/sdk 1.3.0, neonctl 2.36.1). The Python client's vendored v2.json was not refreshed, so the generated enum still carries the older set of actions.

This is the concrete consequence of #18 ("Open API schema out of date"). Note it is also the second occurrence of this exact failure mode: timeline_update_protected_config is present in the enum above, but was previously missing for the same reason.

Impact

Because the failure happens during response validation, it is not recoverable by the caller — branch_create raises before returning anything. For anyone using pytest-neon (which calls branch_create to make ephemeral test branches), this means the entire test suite errors at fixture setup rather than a single call failing.

Suggested fix

Refreshing v2.json and regenerating the types resolves this instance, but the same break recurs on every subsequent addition, since these enum values are additive and the client validates responses strictly.

A more durable option is to make response-side enums tolerant of unknown members, e.g.:

class OperationAction(str, Enum):
    ...

    @classmethod
    def _missing_(cls, value):
        member = object.__new__(cls)
        member._name_ = str(value)
        member._value_ = value
        return member

That keeps known values resolving to real members while letting a value the server introduces pass through instead of raising. Callers that switch on specific actions are unaffected; callers that just read adjacent fields (endpoint_id, status) keep working across schema drift.

We are currently patching this from the outside in our test conftest, which works but is not something every consumer should have to discover independently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions