tldraw/tldraw · error · Error
Tu must be finite and greater than Tc
Error message
Tu must be finite and greater than Tc
What it means
Thrown by the commenting clustering module when Tu (the uncluster threshold — the minimum temporal/spatial separation above which clusters split apart) is not finite or is not greater than Tc. Tu must exceed Tc because clustering contracts below Tc and splits above Tu; Tu <= Tc would collapse the two regimes into a contradiction.
Source
Thrown at packages/commenting/src/clustering/computeClusterTable.ts:63
maxSplitZoom: opts.maxSplitZoom,
})
return { events, leaves: leafNodes }
}
function resolveOptions(options: ClusterOptions): ResolvedClusterOptions {
const Tc = options.Tc ?? 22
const Tu = options.Tu ?? 1.2 * Tc
const eps = options.eps ?? 0.7
const Dmax = options.Dmax ?? 3.75 * Tc
const maxSplitZoom = options.maxSplitZoom ?? 6
const { minZoom, maxZoom } = options
if (!Number.isFinite(Tc) || Tc <= 0) {
throw new Error('Tc must be finite and greater than 0')
}
if (!Number.isFinite(Tu) || Tu <= Tc) {
throw new Error('Tu must be finite and greater than Tc')
}
if (!Number.isFinite(eps) || eps < 0) {
throw new Error('eps must be finite and greater than or equal to 0')
}
if (!Number.isFinite(Dmax) || Dmax < Tc) {
throw new Error('Dmax must be finite and greater than or equal to Tc')
}
if (!Number.isFinite(minZoom) || minZoom <= 0) {
throw new Error('minZoom must be finite and greater than 0')
}
if (!Number.isFinite(maxZoom) || maxZoom <= minZoom) {
throw new Error('maxZoom must be finite and greater than minZoom')
}
if (!Number.isFinite(maxSplitZoom) || maxSplitZoom <= 0) {
throw new Error('maxSplitZoom must be finite and greater than 0')
}
return { Tc, Tu, eps, Dmax, minZoom, maxZoom, maxSplitZoom }View on GitHub (pinned to b31086b447)
Solutions
- Ensure Tu > Tc; if customizing Tc, either omit Tu (so it defaults to 1.2*Tc) or compute Tu from Tc: `Tu = Tc * 1.2`.
- Validate the relationship before calling: `if (!(Tu > Tc)) throw ...` in your own config layer.
- Review config presets to confirm Tu/Tc ordering matches the clustering contract.
- Clamp: `const Tu = Math.max(options.Tu ?? 1.2 * Tc, Tc + 1)`.
Example fix
// before
const Tu = options.Tu ?? 1.2 * Tc
if (!Number.isFinite(Tu) || Tu <= Tc) {
throw new Error('Tu must be finite and greater than Tc')
}
// caller side: derive Tu from Tc so the invariant always holds
const Tc = 40
computeClusterTable(leaves, { Tc, Tu: Tc * 1.2, minZoom, maxZoom }) Defensive patterns
Strategy: validation
Validate before calling
function assertValidTu(Tc: number, options: ClusterOptions): void {
const Tu = options.Tu ?? 1.2 * Tc
if (!Number.isFinite(Tu) || Tu <= Tc) {
throw new Error(`options.Tu must be finite and > Tc (${Tc}). Got ${Tu}. Omit Tu to default to 1.2*Tc.`)
}
} Type guard
function isValidTu(Tu: unknown, Tc: number): boolean {
return typeof Tu === 'number' && Number.isFinite(Tu) && Tu > Tc
} Prevention
- If you customize Tc, omit Tu so it scales automatically (1.2 * Tc).
- Review config presets to confirm Tu > Tc whenever both are set.
- Derive Tu from Tc in your config layer: `Tu = Tc * 1.2`, rather than hardcoding both.
When it happens
Trigger: Passing options.Tu less than or equal to options.Tc, or leaving Tu unset while a custom Tc larger than the default Tu multiple (1.2*Tc) is provided (not possible by default, but a future change could). Explicitly passing Tu = NaN or Infinity. The most common case is Tu <= Tc after both are customized independently.
Common situations: A host tunes Tc up (e.g. to 40) and also sets Tu to a fixed value (e.g. 30) that no longer exceeds Tc. A config preset has Tu and Tc swapped. Tu is derived from a formula that can drop below Tc under certain canvas conditions.
Related errors
- Tc must be finite and greater than 0
- eps must be finite and greater than or equal to 0
- Dmax must be finite and greater than or equal to Tc
- minZoom must be finite and greater than 0
- maxZoom must be finite and greater than minZoom
AI-assisted analysis of tldraw/tldraw@b31086b447 (2026-08-12).
Data as JSON: /api/errors/63733ba1bc8051f8.
Report an issue: GitHub.