Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 5ad4387f96 fix: handle undefined flow state gracefully in workflow run side panel
https://sonarly.com/issue/34209?type=bug

When viewing a workflow run record page, the side panel crashes with "Expected the flow to be defined" because `WorkflowRunVisualizerEffect` resets `flowComponentState` to `undefined` during data refetch while the side panel that depends on this state remains open.

Fix: Replaced `useFlowOrThrow()` with `useAtomComponentStateValue(flowComponentState)` in `SidePanelWorkflowRunViewStepContent` and added `!isDefined(flow)` to the existing null-check guard that already handles `workflowRun` and `workflowSelectedNode` being undefined.

**Why this fix:**
The `flowComponentState` is set by `WorkflowRunVisualizerEffect` via a `useEffect` that depends on async GraphQL data. When the workflow run data is temporarily unavailable (network errors, refetch cycles), the effect resets `flowComponentState` to `undefined` without closing the side panel. The side panel component was using `useFlowOrThrow()` which throws when flow is undefined, causing a crash.

The fix follows the exact same pattern already used on line 68 of the same file:
```typescript
if (!isDefined(workflowRun) || !isDefined(workflowSelectedNode)) {
  return null;
}
```

Now extended to also check for flow:
```typescript
if (!isDefined(flow) || !isDefined(workflowRun) || !isDefined(workflowSelectedNode)) {
  return null;
}
```

This renders nothing (graceful degradation) while the flow data is loading or temporarily unavailable, and re-renders automatically when the Jotai atom is populated — matching how the component already handles the other async dependencies.
2026-05-04 20:01:39 +00:00
@@ -8,8 +8,8 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab
import { type SingleTabProps } from '@/ui/layout/tab-list/types/SingleTabProps';
import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow';
import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun';
import { flowComponentState } from '@/workflow/states/flowComponentState';
import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow';
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
@@ -43,7 +43,7 @@ const StyledTabListContainer = styled.div`
type TabId = WorkflowRunTabIdType;
export const SidePanelWorkflowRunViewStepContent = () => {
const flow = useFlowOrThrow();
const flow = useAtomComponentStateValue(flowComponentState);
const workflowSelectedNode = useAtomComponentStateValue(
workflowSelectedNodeComponentState,
);
@@ -65,7 +65,11 @@ export const SidePanelWorkflowRunViewStepContent = () => {
sidePanelPageComponentInstance.instanceId,
);
if (!isDefined(workflowRun) || !isDefined(workflowSelectedNode)) {
if (
!isDefined(flow) ||
!isDefined(workflowRun) ||
!isDefined(workflowSelectedNode)
) {
return null;
}