strawberry icon indicating copy to clipboard operation
strawberry copied to clipboard

List of union type field always yields the first type

Open pcraciunoiu opened this issue 4 months ago • 2 comments

Describe the Bug

This was originally filed in strawberry-django but it's actually a broader issue with strawberry.

Sample code

from typing import Annotated, Optional, Union
import strawberry, strawberry_django
from django.db.models import Model, CharField


class FruitModel(Model): 
    type = CharField(max_length=10)


@strawberry.interface
class Fruit(strawberry.relay.Node):
    type: str

    @classmethod
    def resolve_type(cls, value: models.TaskTemplate, info: strawberry.Info, parent_type) -> str:
        if value.type == "Apple":
            return "Apple"
        elif value.type == "Banana":
            return "Banana"
        raise TypeError("Invalid fruit type")


@strawberry_django.type(FruitModel, fields=["type"])
class Apple(Fruit):
    pass

@strawberry_django.type(FruitModel, fields=["type"])
class Banana(Fruit):
    pass


FruitUnion = Annotated[Union[Apple, Banana], strawberry.union("FruitUnion")]


@strawberry.type
class Queries:
    @strawberry.field
    def fruit(self, info: Info) -> FruitUnion:
        return FruitModel(type="Banana")

query Fruit {
  fruit {
    __typename
    ... on Fruit {
      type
    }
  }
}

And the output is:

{
  "data": {
    "fruit": {
      "__typename": "Apple",
      "type": "Banana"
    }
  }
}

If I change the return type from FruitUnion to Fruit, the resolve_type method gets called. It DOES NOT get called for the union.

The fix, as suggested in the issue comment, is to define is_type_of, so the updated types would be:

@strawberry_django.type(FruitModel, fields=["type"])
class Apple(Fruit):

    @classmethod
    def is_type_of(cls, root: "Apple", info: strawberry.Info) -> bool:
        return root.type == "Apple"

@strawberry_django.type(FruitModel, fields=["type"])
class Banana(Fruit):
    @classmethod
    def is_type_of(cls, root: "Banana", info: strawberry.Info) -> bool:
        return root.type == "Banana"

System Information

  • Operating system: Ubuntu 25.04
  • Python version: 3.12.11
  • Strawberry version (if applicable): 0.280.0 (latest as of this filing)

Additional Context

The comment from @bellini666 suggests documenting this better. It would be good to update the existing docs so that people don't expect @resolve_type to be called for interfaces. Or maybe make it do so, then is_type_of wouldn't be needed? At any rate it's a bit strange to have to define both.

pcraciunoiu avatar Aug 20 '25 20:08 pcraciunoiu

hi @pcraciunoiu! Thanks for the issue 😊 Can you make a reproduction that doesn't use Strawberry Django? :D

patrick91 avatar Aug 21 '25 08:08 patrick91

@patrick91 I'm not familiar with other types of objects besides a basic interface, and that won't work because we need to be able to return a model/not the direct strawberry type.

What do you suggest?

pcraciunoiu avatar Aug 21 '25 20:08 pcraciunoiu