[Fix] Team UI: handle legacy dict shape for metadata.guardrails (#27224)

* [Fix] Team UI: handle legacy dict shape for metadata.guardrails

A team can have metadata.guardrails stored as {"modify_guardrails": bool}
(the permission-flag shape introduced in PR #4810) rather than the
expected string[]. The opt-out logic added in PR #25575 calls .filter()
on this field, which throws TypeError on a dict and crashes the team
detail page.

Add a safeGuardrailsList helper that returns [] when the field is not
an array, and route the three read sites through it.

* [Fix] Team UI: inline Array.isArray guards for guardrails metadata

Replace the safeGuardrailsList helper with inline Array.isArray checks
at each call site, and apply the same guard to opted_out_global_guardrails
for consistency. No known legacy dict rows for opted_out_global_guardrails,
but the unguarded `|| []` pattern is the same shape risk.

Six call sites now defended directly: three for metadata.guardrails
and three for metadata.opted_out_global_guardrails.
This commit is contained in:
ryan-crabbe-berri
2026-05-05 15:40:44 -07:00
committed by GitHub
parent 7e13256fee
commit 1b25f853ce
@@ -653,10 +653,12 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
const { team_info: info } = teamData;
const initialKillSwitchOn = info.metadata?.disable_global_guardrails === true;
const optedOutGlobals = new Set<string>(info.metadata?.opted_out_global_guardrails || []);
const nonGlobalOptIns: string[] = (info.metadata?.guardrails || []).filter(
(n: string) => !globalGuardrailNames.has(n),
const optedOutGlobals = new Set<string>(
Array.isArray(info.metadata?.opted_out_global_guardrails) ? info.metadata.opted_out_global_guardrails : [],
);
const nonGlobalOptIns: string[] = (
Array.isArray(info.metadata?.guardrails) ? info.metadata.guardrails : []
).filter((n: string) => !globalGuardrailNames.has(n));
const effectiveGuardrails: string[] = initialKillSwitchOn
? nonGlobalOptIns
: [
@@ -815,8 +817,8 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
<Card>
<GuardrailSettingsView
globalGuardrailNames={globalGuardrailNames}
teamGuardrails={info.metadata?.guardrails || []}
optedOutGlobalGuardrails={info.metadata?.opted_out_global_guardrails || []}
teamGuardrails={Array.isArray(info.metadata?.guardrails) ? info.metadata.guardrails : []}
optedOutGlobalGuardrails={Array.isArray(info.metadata?.opted_out_global_guardrails) ? info.metadata.opted_out_global_guardrails : []}
killSwitchOn={initialKillSwitchOn}
variant="inline"
/>
@@ -1619,8 +1621,8 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
<GuardrailSettingsView
globalGuardrailNames={globalGuardrailNames}
teamGuardrails={info.metadata?.guardrails || []}
optedOutGlobalGuardrails={info.metadata?.opted_out_global_guardrails || []}
teamGuardrails={Array.isArray(info.metadata?.guardrails) ? info.metadata.guardrails : []}
optedOutGlobalGuardrails={Array.isArray(info.metadata?.opted_out_global_guardrails) ? info.metadata.opted_out_global_guardrails : []}
killSwitchOn={initialKillSwitchOn}
variant="inline"
className="pt-4 border-t border-gray-200"