Skip to content

Commit b4694b2

Browse files
committed
fix: prevent React error #31 when rendering workflow output objects
Add safelyRenderValue utility function to handle objects, arrays, and other non-primitive types that could be rendered directly in React components. Applied to: - queued-messages.tsx: msg.content - notifications.tsx: notification.message - todo-list.tsx: todo.content Fixes #2725
1 parent e9c4251 commit b4694b2

File tree

4 files changed

+47
-3
lines changed

4 files changed

+47
-3
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/notifications/notifications.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createLogger } from '@sim/logger'
33
import clsx from 'clsx'
44
import { X } from 'lucide-react'
55
import { Button, Tooltip } from '@/components/emcn'
6+
import { safelyRenderValue } from '@/lib/core/utils/formatting'
67
import { useRegisterGlobalCommands } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
78
import { createCommands } from '@/app/workspace/[workspaceId]/utils/commands-utils'
89
import { usePreventZoom } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks'
@@ -147,7 +148,7 @@ export const Notifications = memo(function Notifications() {
147148
{notification.level === 'error' && (
148149
<span className='mr-[6px] mb-[2.75px] inline-block h-[6px] w-[6px] rounded-[2px] bg-[var(--text-error)] align-middle' />
149150
)}
150-
{notification.message}
151+
{safelyRenderValue(notification.message)}
151152
</div>
152153
<Tooltip.Root>
153154
<Tooltip.Trigger asChild>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/queued-messages/queued-messages.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { useCallback, useState } from 'react'
44
import { ArrowUp, ChevronDown, ChevronRight, Trash2 } from 'lucide-react'
5+
import { safelyRenderValue } from '@/lib/core/utils/formatting'
56
import { useCopilotStore } from '@/stores/panel/copilot/store'
67

78
/**
@@ -66,7 +67,9 @@ export function QueuedMessages() {
6667

6768
{/* Message content */}
6869
<div className='min-w-0 flex-1'>
69-
<p className='truncate text-[13px] text-[var(--text-primary)]'>{msg.content}</p>
70+
<p className='truncate text-[13px] text-[var(--text-primary)]'>
71+
{safelyRenderValue(msg.content)}
72+
</p>
7073
</div>
7174

7275
{/* Actions - always visible */}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/todo-list/todo-list.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { memo, useEffect, useState } from 'react'
44
import { Check, ChevronDown, ChevronRight, Loader2, X } from 'lucide-react'
55
import { Button } from '@/components/emcn'
66
import { cn } from '@/lib/core/utils/cn'
7+
import { safelyRenderValue } from '@/lib/core/utils/formatting'
78

89
/**
910
* Represents a single todo item
@@ -148,7 +149,7 @@ export const TodoList = memo(function TodoList({
148149
: 'text-[var(--text-primary)]'
149150
)}
150151
>
151-
{todo.content}
152+
{safelyRenderValue(todo.content)}
152153
</span>
153154
</div>
154155
))}

apps/sim/lib/core/utils/formatting.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,42 @@ export function formatDuration(durationMs: number): string {
161161
const remainingMinutes = minutes % 60
162162
return `${hours}h ${remainingMinutes}m`
163163
}
164+
165+
/**
166+
* Safely converts a value to a string for React rendering.
167+
* Prevents React error #31 by handling objects, arrays, and other non-primitive types.
168+
*
169+
* @param value - The value to convert to a string
170+
* @returns A string representation safe for React rendering
171+
*
172+
* @example
173+
* safelyRenderValue("hello") // "hello"
174+
* safelyRenderValue(123) // "123"
175+
* safelyRenderValue({ text: "hello", type: "text" }) // '{"text":"hello","type":"text"}'
176+
* safelyRenderValue([1, 2, 3]) // "[1,2,3]"
177+
* safelyRenderValue(null) // ""
178+
* safelyRenderValue(undefined) // ""
179+
*/
180+
export function safelyRenderValue(value: unknown): string {
181+
if (value === null || value === undefined) {
182+
return ''
183+
}
184+
185+
if (typeof value === 'string') {
186+
return value
187+
}
188+
189+
if (typeof value === 'number' || typeof value === 'boolean') {
190+
return String(value)
191+
}
192+
193+
if (typeof value === 'object') {
194+
try {
195+
return JSON.stringify(value)
196+
} catch {
197+
return '[Object]'
198+
}
199+
}
200+
201+
return String(value)
202+
}

0 commit comments

Comments
 (0)