Is there a way to know which cell got an error?

I have shared a notebook with remote people, and they get an error when running the notebook, that I cannot reproduce.
It’s very hard to debug remotely, asynchronously, with people that are not directly computer-savvy. They send me a screenshot of the top of the notebook, but I cannot ask them to lose their time screenshooting the notebook part by part scrolling until the end (it’s somewhat long).
Is it possible to get a list of the current errors of the notebook (or the source error of all the other errors), and display it in a dedicated cell? I imagine it would be hard, since the error stops the execution of the rest of the cells, but, it would be nice to be able to display a check cell on top of the page, and get the error message there, so the typical screenshot they send me would help me to look for the problem.

1 Like

I agree this would be a useful feature!

Have you asked your colleagues to send you their console output? That might contain some info about the error. If not, you could add console.log('cell XXX start / end'); at the beginning and end of cells that you think might be troublesome.

Then you could trace through the graphs given by the “Notebook visualizer” tool to find the issue:

I certainly had same situation several times , but console helped me in this situation

It’s hard to debug , when error is happening inside function at runtime which is defined in one cell and error is displaying inside another cell (but not inside of the actual cell, where buggy code is written )

At least , I think, output of cell orders will be huge help when error is happening. And if we could determine place of cell, where error was first thrown, that also would be good

I don’t exactly know Observable’s implementation details though , I suspect this may be hard to implement

You can wrap your top level code in a try/catch and then return the error to display a trace:

3 Likes

@bumbeishvili I highly recommend to enable the option “Pause on exception” in your dev tools. Depending on where there error originates (e.g. in promises) you may also want to enable “Pause on caught exceptions”. The latter may give you a few false positives, but as long as you skip “errors” in the Main thread you should be good.

3 Likes

Hmm, I wonder if it would be possible to replace anonymous - which I guess might be the a wrapping a cell - with an actual function name matching the cell name. Without it the stack trace is also rather opaque:

The cell name (if any) is displayed at the beginning of each line of the trace output in a browser-dependent way:

  • they’re the only things displayed in the trace in Safari
  • they’re right before an @ symbol in Firefox
  • they’re formatted as “at cell_name” in Chrome

Unfortunately that is only true if your cell is a named function.

You can wrap your cell code inside a named IIFE:

triggerPoint = (function triggerPoint(){
  trigger_error();
}())

Parsing or formatting the stack message itself is much more difficult, I’m afraid. Because this feature is non-standard every browser produces a different output.

I’ve updated my notebook with a few more approaches and an experimental logger:

2 Likes

Wow, this is neat! Sadly, the trace function doesn’t seem to work in Firefox or Safari.

The output to faulty_cell2 looks like this in Firefox:

faulty_cell2 = trigger_error@https://static.observableusercontent.com/worker/worker.[snip].js line 2 > eval:3:3
@https://static.observableusercontent.com/worker/worker.[snip].js line 2 > eval:3:3
@https://static.observableusercontent.com/worker/worker.[snip].js line 2 > eval:8:15
@https://static.observableusercontent.com/worker/worker.[snip].js line 2 > eval:2:1
ht/e._promise<@https://static.observableusercontent.com/worker/worker.[snip].js:2:28743

In Safari:

faulty_cell2 = trigger_error


promiseReactionJob@[native code]

(Wrapping them manually in IIFE does though)

1 Like

Thanks for your tests! I’ve added a disclaimer regarding the current browser support, but beyond that there’s probably not much I can do.

I’ve also updated the trace*() functions to throw the error again (but with stack replacing message) which should alleviate some side effects.

@mbostock Any chance that Observable could catch all error events at the window level and rethrow them as CustomEvents, with proper stack and offsets (similar to how you deal with promises)?

1 Like

Great investigation here. We first added our rudimentary error location highlighting to Observable notebooks last year, and haven’t really touched it since — but it would be wonderful to improve.

Ideally, and by default, every error location that exists in an Observable cell, as the error winds its way up to the top through the call stack, would all be highlighted simultaneously in your notebook. Something like this:

Even better would be to be able to both trace the error through your call stack, and highlight especially prominently its first occurrence — even while that same cell maintains a valid definition, and can continue to be used by other parts of the code.

As you all have seen, just by looking at error.stack, it feels pretty hopeless. Although we can know that the last <anonymous> error location is in the top-most cell, anything before that could come from any cell or any library-defined function, and determining which cell the error position should be associated with doesn’t seem possible to do robustly.

But talking it over with Tom, he had a better idea. Perhaps, when an error occurs in a notebook, we can switch the failing cell, along with all of its inputs, into a sort of “debug mode”, where the cell body is wrapped in a try/catch. The catch would simply re-throw the error, but in doing so, would add accurate cell and position information to the error object: {cell: "trigger_error", line: 2, column: 12}. Then when the error eventually arrives at the top-level call, we have all the information we need to highlight through the call stack.

Seems like it’s worth a shot, although no promises about how soon we’ll be able to take a crack at it…

4 Likes

I’m curious how you’d pull off the switching. Wouldn’t each cell have to be wrapped in try/catch by default in order to get proper error reporting without interference/side effects?

I’m not sure what side effects you’re referring to?

Each cell body is already compiled into a function body, returning (or yielding) the value of the cell. I don’t see how wrapping the function body in a try/catch that rethrows would change the semantics of your notebook.

If you’re talking about the side effects of the additional try/catch changing the line/column position of where the error occurs, versus what you see in the cell — that’s quite right, but we already have a sort of source-mapping in place so that we can translate code locations between what you type into the editor and what is actually evaluated in JavaScript. We can use that again here.

Perhaps I misunderstood would you were referring to as “switching”. It sounded to me like you meant reevaluating the cell a second time when an error occurs. I guess you only referred to some kind of change in the UI/editor?

I don’t either, and that’s not what I meant.

Yup, I’m aware. Even considered using it for the above error notebook, but couldn’t quickly figure how to access the source maps.

Ah, right. I do indeed mean to re-evaluate the cell with the instrumenting code added.

I guess that might be problematic if you’re using unclean side effects … especially if the rest of the cell is upstream from other parts of your notebook.

Maybe some kind of notification that gives you the choice to re-evaluate with try-catch or ignore the error? After all, some errors are an inevitable temporary when you write code (for example when you refactor cells) so aside from side-effects, automatically switching to debug mode could be annoying for that reason too.