-
{msg.content}
+
+ {safelyRenderValue(msg.content)}
+
{/* Actions - always visible */}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/todo-list/todo-list.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/todo-list/todo-list.tsx
index 8c865f1c21..1b90101913 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/todo-list/todo-list.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/todo-list/todo-list.tsx
@@ -4,6 +4,7 @@ import { memo, useEffect, useState } from 'react'
import { Check, ChevronDown, ChevronRight, Loader2, X } from 'lucide-react'
import { Button } from '@/components/emcn'
import { cn } from '@/lib/core/utils/cn'
+import { safelyRenderValue } from '@/lib/core/utils/formatting'
/**
* Represents a single todo item
@@ -148,7 +149,7 @@ export const TodoList = memo(function TodoList({
: 'text-[var(--text-primary)]'
)}
>
- {todo.content}
+ {safelyRenderValue(todo.content)}
))}
diff --git a/apps/sim/lib/core/utils/formatting.ts b/apps/sim/lib/core/utils/formatting.ts
index 3fcf3d6f1b..7af8f9ae0c 100644
--- a/apps/sim/lib/core/utils/formatting.ts
+++ b/apps/sim/lib/core/utils/formatting.ts
@@ -161,3 +161,42 @@ export function formatDuration(durationMs: number): string {
const remainingMinutes = minutes % 60
return `${hours}h ${remainingMinutes}m`
}
+
+/**
+ * Safely converts a value to a string for React rendering.
+ * Prevents React error #31 by handling objects, arrays, and other non-primitive types.
+ *
+ * @param value - The value to convert to a string
+ * @returns A string representation safe for React rendering
+ *
+ * @example
+ * safelyRenderValue("hello") // "hello"
+ * safelyRenderValue(123) // "123"
+ * safelyRenderValue({ text: "hello", type: "text" }) // '{"text":"hello","type":"text"}'
+ * safelyRenderValue([1, 2, 3]) // "[1,2,3]"
+ * safelyRenderValue(null) // ""
+ * safelyRenderValue(undefined) // ""
+ */
+export function safelyRenderValue(value: unknown): string {
+ if (value === null || value === undefined) {
+ return ''
+ }
+
+ if (typeof value === 'string') {
+ return value
+ }
+
+ if (typeof value === 'number' || typeof value === 'boolean') {
+ return String(value)
+ }
+
+ if (typeof value === 'object') {
+ try {
+ return JSON.stringify(value)
+ } catch {
+ return '[Object]'
+ }
+ }
+
+ return String(value)
+}
From 959374c2081f3bd307c7e22c03cac7b34c29ca36 Mon Sep 17 00:00:00 2001
From: wingding12 <144286538+wingding12@users.noreply.github.com>
Date: Sat, 24 Jan 2026 12:50:56 -0500
Subject: [PATCH 2/3] refactor: extract text property from structured objects
---
apps/sim/lib/core/utils/formatting.ts | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/apps/sim/lib/core/utils/formatting.ts b/apps/sim/lib/core/utils/formatting.ts
index 7af8f9ae0c..9507a1ff73 100644
--- a/apps/sim/lib/core/utils/formatting.ts
+++ b/apps/sim/lib/core/utils/formatting.ts
@@ -191,6 +191,11 @@ export function safelyRenderValue(value: unknown): string {
}
if (typeof value === 'object') {
+ // Handle common structured output pattern {text: string, type?: string}
+ if ('text' in value && typeof (value as Record