Runtime-Error "Uncaught TypeError: Cannot read property 'value0' of undefined"
CarstenKoenig opened this issue · 11 comments
Hi,
sadly this one is rather complex.
I have an application that does various async stuff on init (loading local storage, querying the backend, etc.) and is using RoutingDuplex for SPA navigation.
There error happens when I reload a certain page - if I navigate there from within the application (so I guess no initial logic) the error does not happen.
The error is thrown in the last line here:
if (reason instanceof Halogen_Hooks_Internal_Eval_Types.Step) {
return Control_Bind.bind(Control_Monad_Free.freeBind)(getState)(function (v1) {
var nextIndex = (function () {
var $69 = (v1.effectCells.index + 1 | 0) < Data_Array.length(v1.effectCells.queue);
if ($69) {
return v1.effectCells.index + 1 | 0;
};
return 0;
})();
var v2 = unsafeGetCell(v1.effectCells.index)(v1.effectCells.queue);
if (v.value0 instanceof Data_Maybe.Just && v2.value0 instanceof Data_Maybe.Just) {
it's the v2
refernce that is undefined
This code seems to be part of the UseState
constructor of the HookM Monad/Algebra if this help.
I'm trying to get a minimal example that causes this problem but right now I could not come up with one.
Is there anything more I can do to help you?
a part of the stack-trace looks like this
at app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:13095
at $tco_loop (app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8903)
at toView (app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8917)
at go (app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8964)
at app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:130
at $tco_loop (app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8903)
at toView (app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8917)
at go (app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8983)
at go (app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5761)
at app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5766
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:13095
$tco_loop @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8903
toView @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8917
go @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8964
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:130
$tco_loop @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8903
toView @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8917
go @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:8983
go @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5761
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5766
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4449
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4813
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5649
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5291
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5291
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:27738
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:27740
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:27752
renderComponentSlot @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:28168
render @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:28174
patch @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:28194
step @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:6694
patchWidget @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:11915
step @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:6694
onThese @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:12002
exports.diffWithIxE @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:6755
patchElem @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:12011
patchElem @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:11981
step @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:6694
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:28232
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:27800
runSync @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4261
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4499
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4813
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4567
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4519
drain @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4291
enqueue @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4312
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4510
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5810
xhr.onload @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:69
setTimeout (async)
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4699
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4813
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5649
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5291
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5291
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:27738
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:27740
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:27752
renderComponentSlot @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:28168
render @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:28174
patch @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:28194
step @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:6694
patchWidget @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:11915
step @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:6694
onThese @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:12002
exports.diffWithIxE @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:6755
patchElem @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:12011
patchElem @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:11981
step @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:6694
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:28232
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:27800
runSync @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4261
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4499
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4813
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4567
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4519
drain @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4291
enqueue @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4312
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4510
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5810
xhr.onload @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:69
load (async)
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:68
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5807
runAsync @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4269
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4504
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4813
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4567
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4519
drain @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4291
enqueue @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4312
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4510
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5810
xhr.onload @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:69
load (async)
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:68
__do @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:5807
runAsync @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4269
run @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4504
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4519
drain @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4291
enqueue @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4312
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:4510
(anonymous) @ app.js?v=LYwJKuTZn4ugPIuYz3BTJgLs2n8z4EpFjM4kEf_aYfk:6915
Show 11 more frames
Just an update - I think I've got the problem and it was most likely my own fault:
I used a captures
on an component/hook-input together with useTickEffect
seems this fired the Step
before the Initialize
and so the effectsQueue is empty.
So is this a bug or expected behavior - to be honest: I don't know - what do you think?
Could you provide a reproducible demo of the issue?
Thanks for the report, @CarstenKoenig! I'll need to take a deeper look when I have a little more time, but this library definitely shouldn't produce runtime errors unless you explicitly do something unsafe, so I consider this a bug that needs to be fixed.
I'll try to recreate a short example - I let you know if I can come up with something quick.
Hooks can be run on try.purescript.org, so some code that fails over there would be ideal for quickly debugging in a shared environment. Thank you!
I tried to recreate it locally - it's actually really hard - a simple nested componente with a simulated Aff (setTimeout to delay a bit) does not recreate the error.
I guess I have to dig through the actual code some more to see what is needed - but this has to wait for tomorrow - it's quite late here sorry.
short update:
there is something really strange going on.
I did not manage to reproduce with a small example, but in my big application I ran into yet another (probably related) problem: it can happen, that i get a Tuple undefined 0
when I do Hooks.useState
(I checked with that Debug.log
function).
Just some hints about what might be happening:
- the app uses routing and on when a route is matched (uses
matchesWith
fromRouting.PushState
) ittell
s the main-component (could call it routing component I guess) about the new route. - the main/routing component will display the pages using slots based on the current route (that might change via the query)
- now during each query there might be an affjax call (to fetch a token if there was none) and the
Hooks.put
for the current-route is after thisAff
-call
Now in exactly this case: the affjax-call happens it seems that the useState
in the component that will eventually be shown will trigger a few times when it will return an undefined/0
Tuple - if the call does not happen everything is fine (useState
will correctly initialize with the given value - no undefined)
In my use case this will trigger a match-exception (I'm using the RemoteData
library and it has this in it's functor-map
instance:
throw new Error("Failed pattern match at Data.RemoteData (line 12, column 1 - line 12, column 90): " + [ showData.constructor.name, v.constructor.name ]);
the original problem seems to be similar - there a affjax-call was waited for inside a useTickEffect
- if I move this into useLifecycleEffect
it does not happen anymore.
Has somebody any clue where I can look further?
BTW versions used:
- halogen - v.5.1.0
- halogen-hooks - v.0.4.3
tried updating the package-set (now halogen is v.5.1.1
) - did not help :(
I narrowed it down some more - please decide if this is an bug or it's me being stupid.
Background: I needed some way to keep track of logged-in users so my idea was to use a Handle-Pattern, put the Handle into a reader-monad (to pass it around) and be somewhat clever with IxQueue
to create a EventSource
components can subscribe to when the status of the login changes.
The problem here seems to be, that the affjax-call I mentioned eventually will update this status and trigger the eventsource and the main/routing component is subscribed to that (I need to know if the current users has certain rights to give access to restricted routes) - that seems to have caused the issue here (if I remove the emit or something similar the bug will go away).
Should I still try to create some sort of minimum-example? It probably would involve all that.
Some details on the implementation
this is the implementation in case someone want to take a look
module Services.Login.Handle
( Handle
, init
, subscribe
, putToken
, readToken
) where
import Prelude
import Control.Error.Util (hush)
import Data.Maybe (Maybe)
import Effect.Aff.Class (class MonadAff)
import Effect.Class (class MonadEffect, liftEffect)
import Effect.Ref as Ref
import Halogen (SubscriptionId)
import Halogen.Hooks (HookM)
import Halogen.Hooks as Hooks
import Halogen.Query.EventSource (EventSource, Finalizer(..), effectEventSource, emit)
import IxQueue as Queue
import Services.Login.Token (JwtToken)
newtype Handle
= Handle
{ setToken :: forall m. MonadEffect m => Maybe JwtToken -> m Unit
, getToken :: forall m. MonadEffect m => Unit -> m (Maybe JwtToken)
, eventSource :: forall m. MonadAff m => String -> EventSource m (Maybe JwtToken)
}
init :: forall m. MonadAff m => Maybe JwtToken -> m Handle
init initToken = do
current <- liftEffect $ Ref.new initToken
-- Queue to broadcast new Token/Users
queue <- liftEffect $ Queue.new
let
setToken :: forall m'. MonadEffect m' => Maybe JwtToken -> m' Unit
setToken token = liftEffect do
Ref.write token current
Queue.broadcast queue token
getToken :: forall m'. MonadEffect m' => Unit -> m' (Maybe JwtToken)
getToken unit = liftEffect $ Ref.read current
eventSource :: forall m'. MonadAff m' => String -> EventSource m' (Maybe JwtToken)
eventSource subId =
effectEventSource \emitter -> do
Queue.on queue subId (emit emitter)
-- on unsubscribe remove the handler from the queue
pure $ Finalizer (void $ Queue.del queue subId)
pure
$ Handle
{ setToken
, getToken
, eventSource
}
subscribe :: forall m. MonadAff m => Handle -> String -> (Maybe JwtToken -> HookM m Unit) -> HookM m SubscriptionId
subscribe (Handle h) subId onTokenChanged = Hooks.subscribe (map onTokenChanged $ h.eventSource subId)
putToken :: forall m. MonadEffect m => Handle -> Maybe JwtToken -> m Unit
putToken (Handle h) token = h.setToken token
readToken :: forall m. MonadEffect m => Handle -> m (Maybe JwtToken)
readToken (Handle h) = h.getToken unit
now with halogen-subscriptions
out I could remove the extra queue
there and either that or the new version together with halogen-6
seems to have solved this issue (at least for me).