Skip to content

fix: @jsii/python-runtime ValueError: Unknown interface#5213

Open
sgwilliams-ebsco wants to merge 2 commits into
aws:mainfrom
sgwilliams-ebsco:feature/unknown-interface
Open

fix: @jsii/python-runtime ValueError: Unknown interface#5213
sgwilliams-ebsco wants to merge 2 commits into
aws:mainfrom
sgwilliams-ebsco:feature/unknown-interface

Conversation

@sgwilliams-ebsco

@sgwilliams-ebsco sgwilliams-ebsco commented Jul 20, 2026

Copy link
Copy Markdown

Hello,

This PR fixes an issue in the Python runtime where JSII raises a ValueError: Unknown interface when accessing a property with a union type that includes a behavioral interfaces and a struct. If the property is set to a known concrete type that implements the behavioral interface, JSII attempts to call _obtain_interface on the struct and raises an exception since the struct is not defined in the _interfaces Map.

Context

When building a CDK aspect to check tags on the aws_cdk.aws_ec2.CfnLaunchTemplate, I encountered an exception calling Token.is_unresolved on the launch_template_data property specifically when the property value is set to an IResolvable. I created a minimal example that reproduces the issue: https://github.com/sgwilliams-ebsco/jsii-unknown-interface-example

    app = App()
    stack = Stack(scope=app)

    condition = CfnCondition(
        stack, "condition", expression=Fn.condition_equals("a", "a")
    )

    ltd = Fn.condition_if(
        condition.logical_id,
        CfnLaunchTemplate.LaunchTemplateDataProperty(),
        CfnLaunchTemplate.LaunchTemplateDataProperty(),
    )

    lt = CfnLaunchTemplate(stack, "launch-template", launch_template_data=ltd)

    assert Token.is_unresolved(lt.launch_template_data) is True # raises ValueError: Unknown interface

The exception is raised when accessing the property value instead of resolving to the correct concrete implementation (aws_cdk.Intrinsic in this case).

What's happening

In resolve(self, kernel, ref), on line 164 of _reference_map.py JSII attempts to build and return an InterfaceDynamicProxy based on the interfaces listed in the ref:

            if ref.interfaces is not None:
                return InterfaceDynamicProxy(
                    [inst] + self.build_interface_proxies_for_ref(ref)
                )
            else:

In build_interface_proxies_for_ref(self, ref: ObjRef), the code calls _obtain_interface(fqn) for every interface listed in ref.interfaces:

ifaces = [_obtain_interface(fqn) for fqn in ref.interfaces or []]

But due to how union types are registered with JSII, the ref.interfaces contains FQNs for Structs that are registered with _data_types and not _interfaces. Calling _obtain_interface on those Structs leads to the ValueError: Unknown interface.

The fix is to filter out any FQNs in the ref.interfaces when the FQN is registered with _data_types.

More Info

I think there is a deeper issue with union types that is leading to Structs being listed in the ref.interfaces in the first place. I researched that process but don't have the depth of understanding to attempt a fix at that level. I included my research in the unit test for this bug.

Related

This PR is related to #5179.

AI Assistance Disclaimer

I used Claude Code to help me find and understand the source of the bug. However, all code changes were written by a human.


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant