From 3b339d13e628a63b917f732509338a6dcb60669f Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Mon, 20 Apr 2026 17:38:25 -0700 Subject: [PATCH] fix(canvas): skip /registry/:id/peers fetch when workspace not online MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The peers endpoint requires a workspace-scoped bearer token (see validateDiscoveryCaller in handlers/discovery.go — designed for agent-to-agent calls). The canvas session doesn't hold that token, so every Details-tab open for a provisioning / failed / offline workspace fired a 401 that cluttered devtools and lit up the error banner even though the real UX here is "no peers — the workspace hasn't booted." Gate the fetch on status ∈ {online, degraded} and render an empty Peers list for everything else. Follow-up: give the canvas a way to see peers for any workspace (admin session should be enough). Tracked separately — this fix just quiets the noise on the common case. Co-Authored-By: Claude Opus 4.7 (1M context) --- canvas/src/components/tabs/DetailsTab.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/canvas/src/components/tabs/DetailsTab.tsx b/canvas/src/components/tabs/DetailsTab.tsx index 0d52794d..7662b49b 100644 --- a/canvas/src/components/tabs/DetailsTab.tsx +++ b/canvas/src/components/tabs/DetailsTab.tsx @@ -58,8 +58,19 @@ export function DetailsTab({ workspaceId, data }: Props) { }, [workspaceId]); useEffect(() => { + // The /registry/:id/peers endpoint requires a workspace-scoped + // bearer token (validateDiscoveryCaller) which the canvas session + // doesn't hold. For a still-provisioning or failed workspace there + // are no peers to show anyway — skip the fetch so the Details tab + // doesn't flood devtools with 401 noise and so the empty Peers + // section renders cleanly. + if (data.status !== "online" && data.status !== "degraded") { + setPeers([]); + setPeersError(null); + return; + } loadPeers(); - }, [loadPeers]); + }, [loadPeers, data.status]); const handleSave = async () => { setSaving(true);