From c175c0f29af434426b1b160856ff4d8c0c5605ee Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Sat, 29 Nov 2025 22:21:24 -0800 Subject: [PATCH] fix: handle corrupted shapes causing "No nearest point found" errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add cleanup routine on editor mount to remove corrupted draw/line shapes that have no points/segments (these cause geometry errors) - Add global error handler to suppress geometry errors from tldraw instead of crashing the entire app - Both fixes ensure old JSON data with corrupted shapes loads gracefully 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/routes/Board.tsx | 72 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/routes/Board.tsx b/src/routes/Board.tsx index 02ed3a3..2701981 100644 --- a/src/routes/Board.tsx +++ b/src/routes/Board.tsx @@ -114,7 +114,38 @@ const customTools = [ export function Board() { const { slug } = useParams<{ slug: string }>() - + + // Global error handler to suppress geometry errors from corrupted shapes + useEffect(() => { + const handleError = (event: ErrorEvent) => { + if (event.error?.message?.includes('nearest point') || + event.error?.message?.includes('No nearest point') || + event.message?.includes('nearest point')) { + console.warn('Suppressed geometry error from corrupted shape:', event.error?.message || event.message) + event.preventDefault() + event.stopPropagation() + return true + } + } + + const handleUnhandledRejection = (event: PromiseRejectionEvent) => { + if (event.reason?.message?.includes('nearest point') || + event.reason?.message?.includes('No nearest point')) { + console.warn('Suppressed geometry promise rejection:', event.reason?.message) + event.preventDefault() + return true + } + } + + window.addEventListener('error', handleError) + window.addEventListener('unhandledrejection', handleUnhandledRejection) + + return () => { + window.removeEventListener('error', handleError) + window.removeEventListener('unhandledrejection', handleUnhandledRejection) + } + }, []) + // Global wheel event handler to ensure scrolling happens on the hovered scrollable element useEffect(() => { const handleWheel = (e: WheelEvent) => { @@ -938,6 +969,45 @@ export function Board() { ChangePropagator, ClickPropagator, ]) + + // Clean up corrupted shapes that cause "No nearest point found" errors + // This typically happens with draw/line shapes that have no points + try { + const allShapes = editor.getCurrentPageShapes() + const corruptedShapeIds: TLShapeId[] = [] + + for (const shape of allShapes) { + // Check draw and line shapes for missing/empty segments + if (shape.type === 'draw' || shape.type === 'line') { + const props = shape.props as any + // Draw shapes need segments with points + if (shape.type === 'draw') { + if (!props.segments || props.segments.length === 0) { + corruptedShapeIds.push(shape.id) + continue + } + // Check if all segments have no points + const hasPoints = props.segments.some((seg: any) => seg.points && seg.points.length > 0) + if (!hasPoints) { + corruptedShapeIds.push(shape.id) + } + } + // Line shapes need points + if (shape.type === 'line') { + if (!props.points || Object.keys(props.points).length === 0) { + corruptedShapeIds.push(shape.id) + } + } + } + } + + if (corruptedShapeIds.length > 0) { + console.warn(`🧹 Removing ${corruptedShapeIds.length} corrupted shapes (draw/line with no points)`) + editor.deleteShapes(corruptedShapeIds) + } + } catch (error) { + console.error('Error cleaning up corrupted shapes:', error) + } // Set user preferences immediately if user is authenticated if (session.authed && session.username) {