tldraw/tldraw · error · Error
Non-finite coordinate for leaf id: ${leaf.id}
Error message
Non-finite coordinate for leaf id: ${leaf.id} What it means
Thrown by mstEdges() (called from computeClusterTable) when a leaf's point.x or point.y is NaN or +/-Infinity. LeafInput.point must be finite — the MST distance math and the cluster centroid/bbox accumulators assume finite coordinates; a single non-finite value propagates Infinity into every distance and silently breaks clustering. The check fires per-leaf in input order.
Source
Thrown at packages/commenting/src/clustering/mst.ts:22
* Return the Euclidean MST over the leaves, ordered by the canonical edge key:
* distance, then normalized endpoint ids.
*/
export function mstEdges(leaves: readonly LeafInput[]): MstEdge[] {
const n = leaves.length
const ids = new Array<string>(n)
const xs = new Float64Array(n)
const ys = new Float64Array(n)
const seen = new Set<string>()
let root = 0
for (let i = 0; i < n; i++) {
const leaf = leaves[i]
if (seen.has(leaf.id)) {
throw new Error(`Duplicate leaf id: ${leaf.id}`)
}
seen.add(leaf.id)
if (!Number.isFinite(leaf.point.x) || !Number.isFinite(leaf.point.y)) {
throw new Error(`Non-finite coordinate for leaf id: ${leaf.id}`)
}
ids[i] = leaf.id
xs[i] = leaf.point.x
ys[i] = leaf.point.y
if (i > 0 && leaf.id < ids[root]) root = i
}
if (n < 2) return []
const inTree = new Uint8Array(n)
const bestD2 = new Float64Array(n)
const bestFrom = new Int32Array(n)
inTree[root] = 1
bestD2.fill(Number.POSITIVE_INFINITY)
bestFrom.fill(-1)
for (let i = 0; i < n; i++) {View on GitHub (pinned to b31086b447)
Solutions
- Filter or repair leaves before calling: skip leaves whose point is not finite, or clamp them to a known-good anchor.
- Fix the upstream geometry resolver so it never emits NaN/Infinity.
- Assert Number.isFinite on every coordinate in the builder as a guard.
Example fix
// before
computeClusterTable([
{ id: 't1', point: { x: NaN, y: 0 } },
], opts)
// after
const clean = leaves.filter(
(l) => Number.isFinite(l.point.x) && Number.isFinite(l.point.y)
)
computeClusterTable(clean, opts) Defensive patterns
Strategy: validation
Validate before calling
const leaves = rawLeaves.filter( (l) => Number.isFinite(l.point.x) && Number.isFinite(l.point.y) ) computeClusterTable(leaves, opts)
Type guard
function hasFinitePoints(leaves: readonly { point: { x: number; y: number } }[]): boolean {
return leaves.every((l) => Number.isFinite(l.point.x) && Number.isFinite(l.point.y))
} Prevention
- Validate pin anchors at the geometry resolver — never emit NaN coordinates.
- Skip or clamp leaves whose anchor failed to resolve rather than forwarding them.
- Add an assertion in the leaf builder: every coordinate is Number.isFinite.
When it happens
Trigger: Calling computeClusterTable with a leaf whose point is { x: NaN, y: 0 }, { x: Infinity, y: 0 }, or any coordinate produced by a failed arithmetic operation upstream. Also reachable via mstEdges() directly.
Common situations: Resolving a pin anchor from a shape whose geometry returned NaN (e.g. zero-size bounding box center before layout); JSON-parsing a missing coordinate as undefined then arithmetic on it; a migration that left point as null/NaN for old records.
Related errors
- Duplicate leaf id: ${leaf.id}
- Tc must be finite and greater than 0
- Tu must be finite and greater than Tc
- eps must be finite and greater than or equal to 0
- Dmax must be finite and greater than or equal to Tc
AI-assisted analysis of tldraw/tldraw@b31086b447 (2026-08-12).
Data as JSON: /api/errors/c17efed05068b965.
Report an issue: GitHub.