diff --git a/packages/ui/src/features/command-center/components/CommandCenterGrid.tsx b/packages/ui/src/features/command-center/components/CommandCenterGrid.tsx
index 32254d828e..22dc13ec35 100644
--- a/packages/ui/src/features/command-center/components/CommandCenterGrid.tsx
+++ b/packages/ui/src/features/command-center/components/CommandCenterGrid.tsx
@@ -1,4 +1,5 @@
import { destroyShellTerminal } from "@posthog/ui/features/terminal/destroyShellTerminal";
+import { ErrorBoundary } from "@posthog/ui/shell/ErrorBoundary";
import { useCallback, useEffect, useRef, useState } from "react";
import { FOCUSABLE_SELECTOR } from "../../../utils/overlay";
import {
@@ -132,7 +133,12 @@ function GridCell({
zoom: zoom !== 1 ? zoom : undefined,
}}
>
-
+
+
+
{isActive && (
diff --git a/packages/ui/src/primitives/action-selector/useActionSelectorState.test.ts b/packages/ui/src/primitives/action-selector/useActionSelectorState.test.ts
new file mode 100644
index 0000000000..48f70d9a11
--- /dev/null
+++ b/packages/ui/src/primitives/action-selector/useActionSelectorState.test.ts
@@ -0,0 +1,62 @@
+import { act, renderHook } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import type { SelectorOption } from "./types";
+import { useActionSelectorState } from "./useActionSelectorState";
+
+function makeProps(
+ overrides: Partial[0]> = {},
+) {
+ return {
+ options: [] as SelectorOption[],
+ multiSelect: false,
+ allowCustomInput: false,
+ hideSubmitButton: false,
+ currentStep: 0,
+ steps: undefined,
+ onSelect: vi.fn(),
+ onMultiSelect: vi.fn(),
+ onStepChange: vi.fn(),
+ onStepAnswer: vi.fn(),
+ ...overrides,
+ };
+}
+
+describe("useActionSelectorState.selectCurrent", () => {
+ it("does not throw when there are no options (empty allOptions)", () => {
+ const { result } = renderHook(() => useActionSelectorState(makeProps()));
+
+ // Pressing Enter with an empty option list must be a no-op, not a crash.
+ expect(() => act(() => result.current.selectCurrent())).not.toThrow();
+ });
+
+ it("does not throw when the highlighted index is stale after options shrink", () => {
+ const options: SelectorOption[] = [
+ { id: "a", label: "A" },
+ { id: "b", label: "B" },
+ { id: "c", label: "C" },
+ ];
+ const { result, rerender } = renderHook(
+ (props: Parameters[0]) =>
+ useActionSelectorState(props),
+ { initialProps: makeProps({ options }) },
+ );
+
+ // Move the highlight to the last option, then remove every option.
+ act(() => result.current.moveUp());
+ rerender(makeProps({ options: [] }));
+
+ expect(() => act(() => result.current.selectCurrent())).not.toThrow();
+ });
+
+ it("still selects the highlighted option when one exists", () => {
+ const onSelect = vi.fn();
+ const options: SelectorOption[] = [{ id: "approve", label: "Approve" }];
+ const { result } = renderHook(() =>
+ useActionSelectorState(makeProps({ options, onSelect })),
+ );
+
+ act(() => result.current.selectCurrent());
+
+ expect(onSelect).toHaveBeenCalledWith("approve");
+ });
+});
diff --git a/packages/ui/src/primitives/action-selector/useActionSelectorState.ts b/packages/ui/src/primitives/action-selector/useActionSelectorState.ts
index 42c7a21239..d36573fd14 100644
--- a/packages/ui/src/primitives/action-selector/useActionSelectorState.ts
+++ b/packages/ui/src/primitives/action-selector/useActionSelectorState.ts
@@ -259,6 +259,7 @@ export function useActionSelectorState({
const selectCurrent = useCallback(() => {
const selected = allOptions[selectedIndex];
+ if (!selected) return;
if (isSubmitOption(selected.id)) {
if (!showSubmitButton) {