Amending an undefined property with a union type with no default member should produce a more specific error #371

Open
opened 2025-12-30 01:23:55 +01:00 by adam · 1 comment
Owner

Originally created by @joshqou on GitHub (Dec 13, 2025).

When a field is typed as Listing or String, Pkl will fail to intuit the type of the value if it inherits from Dynamic. Int will also be intuited correctly, whereas Mapping is also not intuited.

Example:

class ExampleTyped {
    aa: Listing<String> | String
    bb: String
}

typed: ExampleTyped = new {
    aa { "hello" } // aa = new Listing { "hello" } works fine
    bb = "hello"
}

Pkl Error:

– Pkl Error ––
Tried to read property `aa` but its value is undefined.

21 | aa: Listing<String> | String
     ^^
at example.aa (file:///path/to/example.pkl, line 21)

The above error occurred when rendering path `typed.aa` of module `file:///path/to/example.pkl`.

128 | renderer.renderDocument(value)
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (https://github.com/apple/pkl/blob/0.30.1/stdlib/base.pkl#L128)

132 | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
                                                                         ^^^^
at pkl.base#Module.output.bytes (https://github.com/apple/pkl/blob/0.30.1/stdlib/base.pkl#L132)

Version: Pkl 0.30.1 (Linux 6.11.0-1018-azure, native)

Originally created by @joshqou on GitHub (Dec 13, 2025). When a field is typed as `Listing` or `String`, Pkl will fail to intuit the type of the value if it inherits from Dynamic. `Int` will also be intuited correctly, whereas `Mapping` is also not intuited. **Example:** ```pkl class ExampleTyped { aa: Listing<String> | String bb: String } typed: ExampleTyped = new { aa { "hello" } // aa = new Listing { "hello" } works fine bb = "hello" } ``` **Pkl Error:** ``` – Pkl Error –– Tried to read property `aa` but its value is undefined. 21 | aa: Listing<String> | String ^^ at example.aa (file:///path/to/example.pkl, line 21) The above error occurred when rendering path `typed.aa` of module `file:///path/to/example.pkl`. 128 | renderer.renderDocument(value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at pkl.base#Module.output.text (https://github.com/apple/pkl/blob/0.30.1/stdlib/base.pkl#L128) 132 | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8") ^^^^ at pkl.base#Module.output.bytes (https://github.com/apple/pkl/blob/0.30.1/stdlib/base.pkl#L132) ``` Version: `Pkl 0.30.1 (Linux 6.11.0-1018-azure, native)`
Author
Owner

@HT154 commented on GitHub (Dec 13, 2025):

This is actually expected behavior! This is the error you get when you attempt to amend a type with no default value.

This error message doesn't seem like the best for this situation, so I think it's worth keeping this issue open to explore if a better error can be produced in this scenario.

Here's what's actually happening here:

Union types have no implicit default type (and therefore no default value to amend). This is why assigning a value to the property (regardless of type) doesn't error.

You can annotate union types with * to specify a default member. Amending a property that has a union type with a default is equivalent to amending that default. Typically, when a union type consists of an Object subclass like Listing and a non-Object type like String, you should select the Object type as the default.

This code exhibits the behavior you're expecting:

class ExampleTyped {
    aa: *Listing<String> | String
    bb: String
}

In simple cases like this, Pkl might—in theory—be able to intelligently choose a default union member, but it's not possible to do so in all cases, so we require this behavior to be explicitly specified for consistency.

@HT154 commented on GitHub (Dec 13, 2025): This is actually expected behavior! This is the error you get when you attempt to amend a type with no default value. This error message doesn't seem like the best for this situation, so I think it's worth keeping this issue open to explore if a better error can be produced in this scenario. Here's what's actually happening here: Union types have no implicit default type (and therefore no default value to amend). This is why assigning a value to the property (regardless of type) doesn't error. You can annotate union types with `*` to specify a default member. Amending a property that has a union type with a default is equivalent to amending that default. Typically, when a union type consists of an `Object` subclass like `Listing` and a non-`Object` type like `String`, you should select the `Object` type as the default. This code exhibits the behavior you're expecting: ```pkl class ExampleTyped { aa: *Listing<String> | String bb: String } ``` In simple cases like this, Pkl might—in theory—be able to intelligently choose a default union member, but it's not possible to do so in all cases, so we require this behavior to be explicitly specified for consistency.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/pkl#371