{"record":{"id":"bf2baa8b7a7910a5","repo":"halo-dev/halo","slug":"please-select-two-snapshots-to-compare","errorCode":null,"errorMessage":"Please select two snapshots to compare","messagePattern":"Please select two snapshots to compare","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"ui/console-src/components/snapshots/SnapshotDiffContent.vue","lineNumber":35,"sourceCode":"const props = withDefaults(\n  defineProps<{\n    cacheKey: string;\n    name: string;\n    snapshotNames?: string[];\n    getApi: (snapshotName: string) => Promise<ContentWrapper>;\n  }>(),\n  {\n    snapshotNames: () => [],\n  }\n);\n\nconst { name, snapshotNames, cacheKey } = toRefs(props);\n\nconst { data: snapshot, isLoading } = useQuery({\n  queryKey: SNAPSHOT_DIFF_QUERY_KEY(cacheKey, name, snapshotNames),\n  queryFn: async () => {\n    if (snapshotNames.value?.length !== 2) {\n      throw new Error(\"Please select two snapshots to compare\");\n    }\n\n    const newSnapshot = await props.getApi(snapshotNames.value[0]);\n\n    const oldSnapshot = await props.getApi(snapshotNames.value[1]);\n\n    return {\n      old: oldSnapshot,\n      new: newSnapshot,\n    };\n  },\n  onError(err) {\n    if (err instanceof Error) {\n      Toast.error(err.message);\n    }\n  },\n  enabled: computed(() => !!name.value && !!snapshotNames.value?.length),\n});","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/ui/console-src/components/snapshots/SnapshotDiffContent.vue#L17-L53","documentation":"Thrown in the queryFn of SnapshotDiffContent.vue when snapshotNames.length is not exactly 2. The diff view needs exactly two snapshots (new + old) to compare. Notably, the `enabled` computed only checks !!snapshotNames?.length (truthy, i.e. >=1), so selecting exactly one snapshot enables the query but the queryFn throws this message — a guard/`enabled` mismatch that surfaces the error to the user via onError → Toast.","triggerScenarios":"snapshotNames has length 1 (or >2): the enabled predicate is satisfied (truthy length) so useQuery runs, but queryFn asserts length === 2 and throws. Also reachable if the parent transiently passes a single-element array while the user is mid-selection, or if a snapshot list filter reduces the selection to one item reactively.","commonSituations":"User selects only one snapshot in the diff picker and the component renders; parent passes [onlyNew] before [old] is chosen; a watch updates snapshotNames to length 1 during reactivity churn; the diff UI is opened from a context that pre-selects a single snapshot. The onError shows 'Please select two snapshots to compare' as a toast even though the template's v-if (line 228) already shows a select-two tip — double feedback.","solutions":["Fix the `enabled` predicate to require exactly two snapshots so the query never runs in a throwing state: enabled = computed(() => !!name.value && snapshotNames.value?.length === 2).","Ensure the parent only mounts/renders SnapshotDiffContent once two snapshots are chosen.","If you intentionally keep the throw, suppress onError when length !== 2 to avoid duplicate UX feedback (the template already handles the empty/insufficient case visually).","Add a type guard at the parent boundary so snapshotNames is typed as [string, string] only when two are selected."],"exampleFix":"// before\nenabled: computed(() => !!name.value && !!snapshotNames.value?.length),\nqueryFn: async () => {\n  if (snapshotNames.value?.length !== 2) {\n    throw new Error(\"Please select two snapshots to compare\");\n  }\n  ...\n// after — align enabled with the queryFn's precondition\nenabled: computed(() => !!name.value && snapshotNames.value?.length === 2),","handlingStrategy":"validation","validationCode":"// Require exactly two snapshots before the diff query can run\nfunction twoSnapshotNames(names: string[] | undefined): [string, string] | null {\n  if (Array.isArray(names) && names.length === 2) return [names[0], names[1]];\n  return null;\n}\n// use in enabled + queryFn:\n// enabled: computed(() => !!name.value && twoSnapshotNames(snapshotNames.value) !== null)","typeGuard":"function hasTwoSnapshots(names: string[] | undefined): names is [string, string] {\n  return Array.isArray(names) && names.length === 2;\n}","tryCatchPattern":"onError(err) {\n  // Suppress the 'select two' message here — the template already shows select_two_tip\n  if (err instanceof Error && err.message !== \"Please select two snapshots to compare\") {\n    Toast.error(err.message);\n  }\n}","preventionTips":["Make the `enabled` predicate require snapshotNames.length === 2, not merely truthy — this is the core fix that prevents the throw.","Render SnapshotDiffContent only after two snapshots are chosen in the parent.","Type the prop as a 2-tuple when two are selected to encode the invariant at the type level."],"tags":["vue","tanstack-query","snapshots","diff","validation","guard-mismatch"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}