halo-dev/halo · warning · Error

Please select a snapshot

Error message

Please select a snapshot

What it means

Thrown inside a TanStack Query queryFn in SnapshotContent.vue when the `snapshotNames` prop array is empty. It is a defensive guard before calling props.getApi(snapshotNames.value[0]) — accessing index 0 of an empty array would pass undefined to the API. The component's `enabled` computed already requires a non-empty snapshotNames, so under normal flow this throw is unreachable; it exists to fail fast if the query is invoked without a selection.

Source

Thrown at ui/console-src/components/snapshots/SnapshotContent.vue:28

const props = withDefaults(
  defineProps<{
    cacheKey: string;
    name: string;
    snapshotNames?: string[];
    getApi: (snapshotName: string) => Promise<ContentWrapper>;
  }>(),
  {
    snapshotNames: () => [],
  }
);

const { name, snapshotNames, cacheKey } = toRefs(props);

const { data: snapshot, isLoading } = useQuery({
  queryKey: SNAPSHOT_QUERY_KEY(cacheKey, name, snapshotNames),
  queryFn: async () => {
    if (!snapshotNames.value?.length) {
      throw new Error("Please select a snapshot");
    }

    return await props.getApi(snapshotNames.value[0]);
  },
  onError(err) {
    if (err instanceof Error) {
      Toast.error(err.message);
    }
  },
  enabled: computed(() => !!name.value && !!snapshotNames.value?.length),
});

const sanitizedContent = computed(() => {
  return DOMPurify.sanitize(snapshot.value?.content || "");
});
</script>
<template>
  <OverlayScrollbarsComponent

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. Ensure the parent always passes a non-empty snapshotNames when it renders SnapshotContent with a valid name — gate the component render on snapshotNames.length.
  2. If this surfaces in a toast (onError shows err.message), treat it as a state-sync bug: verify the order of reactive updates for name vs snapshotNames.
  3. Avoid manual refetchQueries on SNAPSHOT_QUERY_KEY when no snapshot is selected.
  4. Consider returning early (return null/undefined) instead of throwing so an empty selection is a no-op rather than an error.

Example fix

// before
queryFn: async () => {
  if (!snapshotNames.value?.length) {
    throw new Error("Please select a snapshot");
  }
  return await props.getApi(snapshotNames.value[0]);
},
// after — disable query instead of throwing, since enabled already guards it
enabled: computed(() => !!name.value && !!snapshotNames.value?.length),
queryFn: async () => {
  const first = snapshotNames.value?.[0];
  if (!first) return null;
  return await props.getApi(first);
},
Defensive patterns

Strategy: validation

Validate before calling

// Guard before invoking the API inside queryFn
function getFirstSnapshotName(names: string[] | undefined): string | null {
  return Array.isArray(names) && names.length > 0 ? names[0] : null;
}
// usage:
const first = getFirstSnapshotName(snapshotNames.value);
if (!first) return null;
return await props.getApi(first);

Type guard

function hasSelectedSnapshot(names: string[] | undefined): names is string[] {
  return Array.isArray(names) && names.length > 0;
}

Try / catch

// queryFn onError is already wired; keep the throw, but only toast for genuine API errors
onError(err) {
  if (err instanceof Error && err.message !== "Please select a snapshot") {
    Toast.error(err.message);
  }
}

Prevention

When it happens

Trigger: The useQuery runs (name is truthy) but snapshotNames is empty/undefined at queryFn execution time. Because enabled = !!name && !!snapshotNames?.length, this only fires if the enabled predicate is bypassed (manual refetch, initialData with empty array, or a transient state where name updates before snapshotNames). Selecting zero snapshots in the parent UI and forcing a refetch would also trip it.

Common situations: Parent component binds snapshotNames reactively and there is a window where name resolves but snapshotNames has not loaded; a developer calls queryClient.refetchQueries on this key during a state where no snapshot is picked; the parent passes snapshotNames=() => [] default and never sets it; race between two refs after a snapshot deletion.

Related errors


AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14). Data as JSON: /api/errors/3eccf3fe4518e639. Report an issue: GitHub.