Skip to content

List of union type field always yields the first type #3980

@pcraciunoiu

Description

@pcraciunoiu

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions