Uncaught TypeError: cannnot read properties of undefined (reading '$')

Hi,

I’ve just started getting the above JS error in a current project. I’ve kind of fixed it, but the fix/cause is strange, and wondered if anyone could shed any light on this.

I have a Wizard module, which imports a Breadcrumbs module, and the Wizard module has the following content:

type Msg 
    = NoOp
    | BreadcrumbsMsg Breadcrumbs.Msg

update : Msg -> Model -> (Model, Maybe OutMsg)
update msg model =
    case msg of 
        NoOp ->
            (model, Nothing) 
        BreadcrumbsMsg subMsg ->
            ...

After a lot of Debug/console.logging I eventually narrowed it down to the Msg being passed into Wizard.update. I then noticed that the Msg being passed in should not arrive here, it’s the wrong type:

I know, this should be impossible right? But:

When I traced the value of msg in updates case statement, the value was StartWizardMsg OnLoseFocus, which is a msg from the button that is clicked to start the wizard, and is in a completely different module. The StartWizardMsg should never reach the Wizard.update function, if I tried to pass it in, the compiler would complain, so all I can guess is that something is happening during runtime that shouldn’t be happening.

I remembered something from long ago, where changing the order of the branches in an update function fixed a similar problem, so I switched the order of the msgs in update to be:

update msg model =
    BreadcrumbsMsg subMsg ->
        ...
    NoOp ->
        (model, Nothing)

The TypeError went away, although the wrong msg is still being sent to the update function.

I then realised that the NoOp msg was left over from a refactor, and wasn’t needed, so I removed it - the TypeError came back - add the unused NoOp msg back and the TypeError goes away.

All very strange… hope I’ve explained all that okay.

Thoughts anyone?

I encountered this once, like 2 years ago at work and fixed it by adding a unit value () to a message, like

-- before
type Msg
    = MyMessage

-- after
type Msg
    = MyMessage ()

I was never certain of the cause but I think it had to do with web components and events being sent in an unexpected order.

The other part that related to it is that this only showed in production because --optimize changes the compiled value of a message from { "$": "MyMessage", ... } to { "$": 0, ... } where the number matches the ordering of the definition of the message variants. E.g.

type PageOneMsg = A | B

type PageTwoMsg = C | D

compiles to (roughly) when --optimize is used

// PageOneMsg
{ $: 0, ... } // and
{ $: 1, ... }

// PageTwoMsg
{ $: 0, ... } // and
{ $: 1, ... }

meaning that the messages are indistinguishable.


Another possibility is a bug with how Html.map works. I’ve never encountered this but have heard of it from a couple people. @lydell might know more about this.

3 Likes

A message of the wrong type appearing in an update function does sound like the Html.map bug. elm/virtual-dom#105 elm/virtual-dom#162 elm/virtual-dom#171 elm/virtual-dom#166 (PR) elm/html#160 elm/compiler#2069

Some people encounter it, some are lucky and don’t.

If you want, you can help test my project that (amongst other things) fixes this bug: GitHub - lydell/elm-safe-virtual-dom: A robust virtual DOM for Elm.

4 Likes