block/buzz · error · Error

Choose a channel first.

Error message

Choose a channel first.

What it means

WorkflowMessagePicker's infinite history query only runs when `channelId` is truthy (`enabled: Boolean(channelId)`), but the queryFn re-checks and throws 'Choose a channel first.' as a defensive guard. This means the fetch function was invoked without a channel id — normally impossible while `enabled` gates it, so the throw surfaces a state/timing bug or a direct call rather than an expected condition.

Source

Thrown at desktop/src/features/workflows/ui/WorkflowMessagePicker.tsx:76

  onEscape?: () => void;
  value: string;
}) {
  const optionRefs = React.useRef(new Map<string, HTMLButtonElement>());
  const [query, setQuery] = React.useState("");
  const [activeIndex, setActiveIndex] = React.useState<number | null>(null);
  const trimmedQuery = query.trim();
  const deferredQuery = React.useDeferredValue(trimmedQuery);
  const normalizedQuery = deferredQuery.toLowerCase();
  const selectedId = normalizeMessageEventId(value);
  const directId = normalizeMessageEventId(query);
  const lookupId = directId ?? selectedId;

  const historyQuery = useInfiniteQuery({
    enabled: Boolean(channelId),
    initialPageParam: null as ChannelPageCursor | null,
    queryKey: ["workflow-message-picker", channelId],
    queryFn: async ({ pageParam }) => {
      if (!channelId) throw new Error("Choose a channel first.");
      return parseChannelWindowResponse(
        await getChannelWindowEvents(channelId, pageParam, PAGE_SIZE),
        channelId,
        pageParam,
      );
    },
    getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
    staleTime: 30_000,
  });
  const searchQuery = useSearchMessagesQuery(deferredQuery, {
    channelId: channelId ?? undefined,
    enabled: Boolean(channelId && normalizedQuery && !directId),
    limit: 30,
    minimumQueryLength: 1,
  });
  const exactQuery = useQuery({
    enabled: Boolean(channelId && lookupId),
    queryKey: ["workflow-message-picker-exact", channelId, lookupId],

View on GitHub (pinned to dad5a33865)

Solutions

  1. Select a channel so `channelId` is set before triggering the history query or refetch.
  2. Gate any manual `refetch()` on `Boolean(channelId)` so it is never called without a channel.
  3. Check why React Query ran the query despite `enabled: Boolean(channelId)` — look for imperative queryClient.fetchQuery calls bypassing the gate.
  4. In tests, provide a channelId fixture or mock the query before rendering the picker.

Example fix

// before
if (!channelId) throw new Error("Choose a channel first.");
// after — caller guards before refetch
const pickChannel = async () => {
  if (!channelId) return; // UI already requires a selection
  await historyQuery.refetch();
};
Defensive patterns

Strategy: validation

Validate before calling

if (!channelId) return null; // or render a 'pick a channel' placeholder
const historyQuery = useInfiniteQuery({
  enabled: Boolean(channelId),
  queryKey: ["workflow-message-picker", channelId],
  queryFn: ({ pageParam }) => getChannelWindowEvents(channelId!, pageParam, PAGE_SIZE),
});

Type guard

const hasChannel = (id: string | null | undefined): id is string =>
  typeof id === "string" && id.length > 0;

Try / catch

try {
  await historyQuery.refetch();
} catch (e) {
  if (e instanceof Error && e.message === "Choose a channel first.") return;
  throw e;
}

Prevention

When it happens

Trigger: Invoking `historyQuery.refetch()` (or the queryFn) while `channelId` is null/undefined; the picker being rendered and forced to fetch before a channel is selected; code that manually calls the query function outside React Query's enabled gating.

Common situations: Opening the workflow message picker before selecting a channel and triggering a refetch; remount race where stale query state re-fires the queryFn; automated tests calling the query function directly; channelId cleared (channel deleted or switched) while a refetch is in flight.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of block/buzz@dad5a33865 (2026-08-30). Data as JSON: /api/errors/c621b0e0e93b9c8d. Report an issue: GitHub.