2

When cola.js fails, how do you find out what went wrong?

I don't have a minimal reproducible example, because that will likely take hours of debugging to make, and if I had it, then it wouldn't be an example of what I'm asking about. I'm hoping to hear of a way to quickly find out what's gone wrong. Presumably it's some bad data in my graph. But how can I tell what the bad data is?

The screenshot below illustrates a typical situation where something has gone wrong. Various NaNs are reported, there are a bunch of Uncaught TypeErrors, and an Assertion fails. There are a whole lot of local variables, all named with a single letter. Clicking and looking at them shows that a NaN got into a coordinate somewhere, as suggested by the previous error messages. It looks like sometime earlier, something got a null when it needed some sort of list-like data structure. How can I quickly find out what was the bad data that got into the graph?

I'm using cola.v3.min.js.

Screenshot of error shown in console of Chrome debugger


Details

I'm looking for a general approach to finding this kind of error, but here's the specific data that produced the errors in this example. Each line is the argument passed to cy.add() (as suggested by Stepan T.—and printing out everything passed to cy.add() might turn out to be the first step of the answer!).

{"group":"nodes","data":{"id":1,"label":"Workspace","parent":[]},"position":{"x":45,"y":0}}
{"group":"nodes","data":{"id":2,"label":"Target(121)","parent":[1]},"position":{"x":5,"y":0}}
{"group":"nodes","data":{"id":3,"label":"WantFullySourced","parent":[]},"position":{"x":50,"y":0}}
{"group":"nodes","data":{"id":4,"label":"Brick(120)","parent":[1]},"position":{"x":10,"y":0}}
{"group":"nodes","data":{"id":5,"label":"Avail","parent":[]},"position":{"x":55,"y":0}}
{"group":"nodes","data":{"id":6,"label":"Brick(1)","parent":[1]},"position":{"x":15,"y":0}}
{"group":"nodes","data":{"id":7,"label":"Avail","parent":[]},"position":{"x":60,"y":0}}
{"group":"nodes","data":{"id":8,"label":"Brick(2)","parent":[1]},"position":{"x":20,"y":0}}
{"group":"nodes","data":{"id":9,"label":"Avail","parent":[]},"position":{"x":65,"y":0}}
{"group":"nodes","data":{"id":10,"label":"Brick(3)","parent":[1]},"position":{"x":25,"y":0}}
{"group":"nodes","data":{"id":11,"label":"Avail","parent":[]},"position":{"x":70,"y":0}}
{"group":"nodes","data":{"id":12,"label":"Brick(4)","parent":[1]},"position":{"x":30,"y":0}}
{"group":"nodes","data":{"id":13,"label":"Avail","parent":[]},"position":{"x":75,"y":0}}
{"group":"nodes","data":{"id":14,"label":"Brick(5)","parent":[1]},"position":{"x":35,"y":0}}
{"group":"nodes","data":{"id":15,"label":"Avail","parent":[]},"position":{"x":80,"y":0}}
{"group":"nodes","data":{"id":16,"label":"NumericalRelationScout","parent":[1]},"position":{"x":40,"y":0}}
{"group":"nodes","data":{"id":17,"label":"OperandView","parent":[1]},"position":{"x":0,"y":0}}
{"group":"edges","data":{"id":"2.member_of.1.members","source":2,"source_port_label":"member_of","target":1,"target_port_label":"members","weight":0.5}}
{"group":"edges","data":{"id":"4.member_of.1.members","source":4,"source_port_label":"member_of","target":1,"target_port_label":"members","weight":0.5}}
{"group":"edges","data":{"id":"6.member_of.1.members","source":6,"source_port_label":"member_of","target":1,"target_port_label":"members","weight":0.5}}
{"group":"edges","data":{"id":"8.member_of.1.members","source":8,"source_port_label":"member_of","target":1,"target_port_label":"members","weight":0.5}}
{"group":"edges","data":{"id":"10.member_of.1.members","source":10,"source_port_label":"member_of","target":1,"target_port_label":"members","weight":0.5}}
{"group":"edges","data":{"id":"12.member_of.1.members","source":12,"source_port_label":"member_of","target":1,"target_port_label":"members","weight":0.5}}
{"group":"edges","data":{"id":"14.member_of.1.members","source":14,"source_port_label":"member_of","target":1,"target_port_label":"members","weight":0.5}}
{"group":"edges","data":{"id":"16.member_of.1.members","source":16,"source_port_label":"member_of","target":1,"target_port_label":"members","weight":0.5}}
{"group":"edges","data":{"id":"17.member_of.1.members","source":17,"source_port_label":"member_of","target":1,"target_port_label":"members","weight":0.5}}
{"group":"edges","data":{"id":"3.taggees.2.tags","source":3,"source_port_label":"taggees","target":2,"target_port_label":"tags","weight":0.5}}
{"group":"edges","data":{"id":"2.support_from.3.support_to","source":2,"source_port_label":"support_from","target":3,"target_port_label":"support_to","weight":2}}
{"group":"edges","data":{"id":"3.support_from.2.support_to","source":3,"source_port_label":"support_from","target":2,"target_port_label":"support_to","weight":2}}
{"group":"edges","data":{"id":"5.taggees.4.tags","source":5,"source_port_label":"taggees","target":4,"target_port_label":"tags","weight":0.5}}
{"group":"edges","data":{"id":"4.support_from.5.support_to","source":4,"source_port_label":"support_from","target":5,"target_port_label":"support_to","weight":2}}
{"group":"edges","data":{"id":"7.taggees.6.tags","source":7,"source_port_label":"taggees","target":6,"target_port_label":"tags","weight":0.5}}
{"group":"edges","data":{"id":"6.support_from.7.support_to","source":6,"source_port_label":"support_from","target":7,"target_port_label":"support_to","weight":2}}
{"group":"edges","data":{"id":"9.taggees.8.tags","source":9,"source_port_label":"taggees","target":8,"target_port_label":"tags","weight":0.5}}
{"group":"edges","data":{"id":"8.support_from.9.support_to","source":8,"source_port_label":"support_from","target":9,"target_port_label":"support_to","weight":2}}
{"group":"edges","data":{"id":"11.taggees.10.tags","source":11,"source_port_label":"taggees","target":10,"target_port_label":"tags","weight":0.5}}
{"group":"edges","data":{"id":"10.support_from.11.support_to","source":10,"source_port_label":"support_from","target":11,"target_port_label":"support_to","weight":2}}
{"group":"edges","data":{"id":"13.taggees.12.tags","source":13,"source_port_label":"taggees","target":12,"target_port_label":"tags","weight":0.5}}
{"group":"edges","data":{"id":"12.support_from.13.support_to","source":12,"source_port_label":"support_from","target":13,"target_port_label":"support_to","weight":2}}
{"group":"edges","data":{"id":"15.taggees.14.tags","source":15,"source_port_label":"taggees","target":14,"target_port_label":"tags","weight":0.5}}
{"group":"edges","data":{"id":"14.support_from.15.support_to","source":14,"source_port_label":"support_from","target":15,"target_port_label":"support_to","weight":2}}

The object passed to cy.layout() had a circular structure, so JSON.stringify() wouldn't print it. Adding the getCircularReplacer() function recommended here seemed to crash the browser. But manually removing elements from the layout object exposed the error: in a relative alignment constraint (documented under API here), I had an offset of '0' where I needed a 0, i.e. a string where a number was needed.

OK, happily that problem is now fixed. That still leaves the original question: is there a faster way to find errors like that? (The static-typing advocates are surely cackling by now.)

Ben Kovitz
  • 4,920
  • 1
  • 22
  • 50
  • Can you show us an example data set, where these errors occur? Also, what is the property next? Do you use it in the cytoscape stylesheet or in the cola.js settings? – Stephan T. Dec 03 '19 at 16:41
  • @StephanT. The `next` property is invoked by the function `findYNeighbors` deep in cola.js, apparently after trying to parse something with a regular expression (line 3712 of cola.js). My own code has no `next` anywhere. I figure the error probably occurred much earlier, in some little goof somewhere in my data. About the data set: is there a convenient way to print out the data set to see where an error might be? That itself might be the answer to this question. :) – Ben Kovitz Dec 03 '19 at 17:33
  • Well, you have to call `cy.add(...)` somewhere, right? Eiter print that out in the console or print out whatever you put into the elements property of cytoscape – Stephan T. Dec 03 '19 at 17:43
  • Look at these links for better explanations: [elements](https://js.cytoscape.org/#getting-started/specifying-basic-options) or [cy.add()](https://js.cytoscape.org/#cy.add) – Stephan T. Dec 03 '19 at 17:45
  • @StephanT. I just posted all the data passed through `cy.add()`. In just a moment, I'll add the dictionary passed to `cy.layout()`. – Ben Kovitz Dec 03 '19 at 18:07
  • 1
    @StephanT. Hmm, `JSON.stringify()` is complaining that the layout dictionary is a circular structure. I'm now googling to see how to print it out anyway… – Ben Kovitz Dec 03 '19 at 18:21
  • One question though: Do you add the nodes/edges with cy.add() one by one or in a batch? I tried it [here](https://codepen.io/steevokovsky/pen/ExaaWYQ) and it seems to work with a few adjustments – Stephan T. Dec 03 '19 at 20:56
  • 1
    @StephanT. One by one. Thanks for checking the nodes/edges, though! I just solved it moments ago: the error was in the layout object, now explained in the question. Is there some way to do something analogous to a static type check to find little mistakes like that early? – Ben Kovitz Dec 03 '19 at 21:15
  • 1
    Maybe this [thread](https://stackoverflow.com/questions/46257042/handle-errors-with-cytoscape-js) would help? – Stephan T. Dec 04 '19 at 06:35

0 Answers0