Got stuck debugging a broken notebook. Update: mootari fixed it for me.

My SCPie notebook was working just fine when it was last republished in October,

But now it seems to be broken in both Chrome and Safari, including versions of those browsers older than a few months. I get a whole bunch of RangeError: Maximum call stack size exceeded. errors on plots that previously rendered perfectly fine. The notebook hasn’t changed, and the browser hasn’t changed, and I don’t think any of the dependencies changed, so I assume something changed on the Observable platform side.

I am happy to admit that I should someday rewrite my curve rendering code to not be recursive, since relying on the browser call stack is generally problematic.

But in the mean time, do you folks at Observablehq know what changed, and if there’s any quick workaround that doesn’t involve rewriting my code right away?

1 Like

There’s nothing I can think of that we’ve done that would cause this. Have you tried downloading the tarball for your notebook and running it locally? If you do that, you could also run it against older versions of the Observable runtime. But the changes there are relatively minor and uninteresting, so it doesn’t seem likely that runtime changes would affect your notebook.

Hmm. Still seems broken with previous versions of runtime.js. So I’m pretty confused, because I am quite sure it was working a couple months ago. I’ll hopefully get some more time to debug tomorrow.

Or maybe rewrite my bezplot code to use an explicit loop with a manually constructed stack.

Did you recently make changes to https://observablehq.com/@jrus/circle-arc-interpolation#circle_interp that could have increased the call stack size?

I couldn’t see any changes to any of the code dependencies.

I’ll try to spend some today or next week rewriting my bezplot code to not be recursive.

Happened to come across this again and dug in a bit:

  1. The problem originates from the spokespath(1) call.
  2. spokespath calls spokes(1), which calls linspace with parameters n=1 and endpoint=false.
  3. Because endpoint=false, linspace decrements n to 0.
  4. This causes t to become NaN.
  5. NaN carries over to circle_interp: circle_interp([0, 0], [NaN, NaN], [NaN, NaN])
  6. Ultimately _bezeval gets called with x0, x3, x6, y0, y3, and y6 all having a value of NaN.
  7. _bezeval calls itself recursively in const left = _bezeval(...) and … :infinity: :skull:
2 Likes

Aha, thanks! I tried a few times to debug this but kept getting distracted.

For the record: there was no change to the call stack. But I should still probably rewrite bezplot to not use recursive function calls. (And also probably to better detect NaNs.)