diff --git a/.server-changes/span-accessory-text-guard.md b/.server-changes/span-accessory-text-guard.md
new file mode 100644
index 0000000000..ab668efd17
--- /dev/null
+++ b/.server-changes/span-accessory-text-guard.md
@@ -0,0 +1,6 @@
+---
+area: webapp
+type: fix
+---
+
+Prevent dashboard crash (React error #31) when span accessory item text is not a string. Filters out malformed accessory items in SpanCodePathAccessory instead of passing objects to React as children.
diff --git a/apps/webapp/app/components/runs/v3/SpanTitle.tsx b/apps/webapp/app/components/runs/v3/SpanTitle.tsx
index 4c25fc7b9a..0b9273cd48 100644
--- a/apps/webapp/app/components/runs/v3/SpanTitle.tsx
+++ b/apps/webapp/app/components/runs/v3/SpanTitle.tsx
@@ -55,20 +55,24 @@ function SpanAccessory({
case "pills": {
return (
- {accessory.items.map((item, index) => (
-
- ))}
+ {accessory.items
+ .filter((item) => typeof item.text === "string")
+ .map((item, index) => (
+
+ ))}
);
}
default: {
return (
- {accessory.items.map((item, index) => (
-
- {item.text}
-
- ))}
+ {accessory.items
+ .filter((item) => typeof item.text === "string")
+ .map((item, index) => (
+
+ {item.text}
+
+ ))}
);
}
@@ -104,16 +108,18 @@ export function SpanCodePathAccessory({
className
)}
>
- {accessory.items.map((item, index) => (
-
- {item.text}
- {index < accessory.items.length - 1 && (
-
-
-
- )}
-
- ))}
+ {accessory.items
+ .filter((item) => typeof item.text === "string")
+ .map((item, index, filtered) => (
+
+ {item.text}
+ {index < filtered.length - 1 && (
+
+
+
+ )}
+
+ ))}
);
}