From c5ecd2c33d56d220ee79e667392b65ab313b99ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 18 Mar 2026 10:04:09 +0100 Subject: [PATCH] throw Error: invalid dodge radius: -1 instead of crashing with a stack overflow. closes #1912 --- src/transforms/dodge.js | 1 + test/transforms/dodge-test.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 test/transforms/dodge-test.ts diff --git a/src/transforms/dodge.js b/src/transforms/dodge.js index 23c20bd807..5dcaa499e3 100644 --- a/src/transforms/dodge.js +++ b/src/transforms/dodge.js @@ -71,6 +71,7 @@ function dodge(y, x, anchor, padding, r, options) { if (!channels[x]) throw new Error(`missing channel: ${x}`); ({[x]: X} = applyPosition(channels, scales, context)); const cr = R ? undefined : r !== undefined ? number(r) : this.r !== undefined ? this.r : 3; + if (cr !== undefined && !(cr > 0)) throw new Error(`invalid dodge radius: ${cr}`); if (R) R = valueof(R.value, scales[R.scale] || identity, Float64Array); let [ky, ty] = anchor(dimensions); const compare = ky ? compareAscending : compareSymmetric; diff --git a/test/transforms/dodge-test.ts b/test/transforms/dodge-test.ts new file mode 100644 index 0000000000..07e14fc0d1 --- /dev/null +++ b/test/transforms/dodge-test.ts @@ -0,0 +1,12 @@ +import * as Plot from "@observablehq/plot"; +import assert from "assert"; +import it from "../jsdom.js"; + +it("dodgeY throws on non-positive constant r", () => { + assert.throws(() => Plot.dot([1, 2, 3], Plot.dodgeY({x: Plot.identity, r: -1})).plot(), /invalid dodge radius/); + assert.throws(() => Plot.dot([1, 2, 3], Plot.dodgeY({x: Plot.identity, r: 0})).plot(), /invalid dodge radius/); +}); + +it("dodgeX throws on non-positive constant r", () => { + assert.throws(() => Plot.dot([1, 2, 3], Plot.dodgeX({y: Plot.identity, r: -1})).plot(), /invalid dodge radius/); +});