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>
<OverlayScrollbarsComponentView on GitHub (pinned to d2f5165f9c)
Solutions
- Ensure the parent always passes a non-empty snapshotNames when it renders SnapshotContent with a valid name — gate the component render on snapshotNames.length.
- 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.
- Avoid manual refetchQueries on SNAPSHOT_QUERY_KEY when no snapshot is selected.
- 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
- Gate SnapshotContent rendering in the parent on snapshotNames.length > 0.
- Align the useQuery `enabled` predicate with the queryFn's precondition so the query never runs in a throwing state.
- Prefer returning null over throwing for 'empty selection' states — reserve throws for true API failures.
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
- Please select two snapshots to compare
- Attachment has no permalink
- Policy name is required
- File is required
- No permission to upload attachment
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/3eccf3fe4518e639.
Report an issue: GitHub.