Skip to content

[Bug]: Synthesized ucp metadata oneOf can never be satisfied #73

Description

@chaz8081

Describe the bug

ucp.json declares no oneOf in the spec — its root carries only $id, $schema, title, description and $defs. normalize_metadata_schemas synthesizes one, selecting members by name:

ucp["oneOf"] = [
    {"$ref": f"#/$defs/{name}"} for name in metadata_union_members(ucp)
]
return [
    name for name in defs
    if name in ("platform_schema", "business_schema")
    or (name.startswith("response_") and name.endswith("_schema"))
]

Three of the six selected members — response_cart_schema, response_catalog_schema and response_order_schema — have identical bodies, differing only in title and description, which do not affect validation.

oneOf means exactly one. Any instance matching one of those three matches all three, so no input can satisfy the union. Since ucp is a required property on cart, checkout and order, no instance of those three types can validate.

To Reproduce

Verified against ucp 5a5d9e1 and python-sdk a0d8308. Setup as in #72, then:

ucp = json.load(open(root + "/ucp.json"))
v = Draft202012Validator(ucp, registry=reg)
print([e.message for e in v.iter_errors({"version": "2026-04-08"})])
{'version': '2026-04-08'} is valid under each of
  {'$ref': '#/$defs/response_cart_schema'},
  {'$ref': '#/$defs/response_catalog_schema'},
  {'$ref': '#/$defs/response_order_schema'}

Every input fails the same way, including instances carrying capabilities, services or payment_handlers.

Expected behavior

The docstring gives the goal as "a unified ucp metadata property across the entire SDK" — a type union for code generation rather than an exclusivity constraint. anyOf expresses that without asserting distinguishability that nothing verifies:

ucp["anyOf"] = [...]

If oneOf is intended, the members need to be distinguishable — either by deduplicating the three response profiles or by giving each a discriminating property. That would be a spec change; switching to anyOf is not.

Additional context

This predates the current membership rule. Before a763549 the list was hardcoded and already contained both response_cart_schema and response_order_schema — two identical members is already enough to make oneOf unsatisfiable. That commit fixed a real omission (catalog responses were being dropped from every model's ucp field) and, as a side effect, took the duplicate count from two to three.

Why it may not have surfaced. ucp.json is among the documents affected by #72, so eagerly-compiling validators reject it before ever reaching the union. Downstream, pydantic's Union resolves to the first matching member rather than enforcing oneOf, so the generated Python models behave sensibly regardless. It was found while building a Go SDK from the same preprocessed schemas, where enforcing oneOf strictly made Cart, Checkout and Order permanently invalid.

Related but distinct: ucp#615 also concerns oneOf member shapes, in the opposite direction — fulfillment_destination's members compose postal_address inconsistently, forcing consumers to branch. This issue is about members that are indistinguishable. Both bear on whether a oneOf's alternatives are meaningfully different; neither fix addresses the other.

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