From a0db4072ef384a4cf7b740051cc31b1e5bb2bc58 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Tue, 23 Jun 2026 15:04:46 -0700 Subject: [PATCH] fix(search,eval): alias-hop injected results carry page_id (contradiction-probe crash) applyAliasHop injected synthetic SearchResults without page_id (the `as SearchResult` cast hid the missing field), so listActiveTakesForPages bound undefined/NaN into ANY($1::int[]) and crashed the whole contradiction probe on real Postgres. Stamp page_id=page.id at the injection site and add a finite-id filter in generateIntraPagePairs as a defensive backstop (mirrors hybrid.ts:63). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/core/eval-contradictions/runner.ts | 12 ++++++++++-- src/core/search/hybrid.ts | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/core/eval-contradictions/runner.ts b/src/core/eval-contradictions/runner.ts index 7a16728af..adee2c5ef 100644 --- a/src/core/eval-contradictions/runner.ts +++ b/src/core/eval-contradictions/runner.ts @@ -171,11 +171,19 @@ async function generateIntraPagePairs( results: SearchResult[], ): Promise { if (results.length === 0) return []; - // Unique page_ids only. - const pageIds = Array.from(new Set(results.map((r) => r.page_id))); + // Unique, FINITE page_ids only. Defensive backstop for the alias-hop bug + // (#2339 sibling): an alias-injected synthetic result with an undefined/NaN + // page_id must never reach `ANY($1::int[])` — postgres.js rejects it with + // UNDEFINED_VALUE and aborts the whole probe. Mirrors the hybrid.ts:63 filter. + const pageIds = Array.from( + new Set( + results.map((r) => r.page_id).filter((n): n is number => typeof n === 'number' && Number.isFinite(n)), + ), + ); const takesByPage = await engine.listActiveTakesForPages(pageIds); const out: ContradictionPair[] = []; for (const r of results) { + if (typeof r.page_id !== 'number' || !Number.isFinite(r.page_id)) continue; const takes = takesByPage.get(r.page_id) ?? []; if (takes.length === 0) continue; const chunkMember = searchResultToMember(r); diff --git a/src/core/search/hybrid.ts b/src/core/search/hybrid.ts index ee1b08c6e..27da6b8c1 100644 --- a/src/core/search/hybrid.ts +++ b/src/core/search/hybrid.ts @@ -647,6 +647,11 @@ export async function applyAliasHop( if (!page) continue; injectScore += 1e-6; out.push({ + // #2339-sibling: include page_id. The `as SearchResult` cast hid its + // absence, so any consumer reading page_id off an alias-injected result got + // undefined — e.g. listActiveTakesForPages bound undefined/NaN into + // ANY($1::int[]) and crashed the contradiction probe on real Postgres. + page_id: page.id, slug: page.slug, title: page.title, type: page.type,