[object Object]

← back to Norma

fix(api): cast org_id param as ::uuid not ::text in pipeline/stats + settings

eb21d0d7a3011b62a3e36e249586f671f8004bc8 · 2026-05-20 07:51:18 -0700 · Steve Abrams

petition_pipeline.org_id, drafts.org_id, news_items.org_id, collaborations.org_id,
partners.org_id, old_leads.org_id are all uuid columns. The previous $1::text NULL
guard caused pg to infer the bound param as text, then the org_id = $1 comparison
threw 'operator does not exist: uuid = text' for any non-null orgId.

/api/pipeline/stats was 500-ing for SDCC staff. /api/settings would have done the
same had the .catch() not been swallowing it silently into a 0-row default.

Cast both occurrences as ::uuid so the null-guard and the comparison agree.

Files touched

Diff

commit eb21d0d7a3011b62a3e36e249586f671f8004bc8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed May 20 07:51:18 2026 -0700

    fix(api): cast org_id param as ::uuid not ::text in pipeline/stats + settings
    
    petition_pipeline.org_id, drafts.org_id, news_items.org_id, collaborations.org_id,
    partners.org_id, old_leads.org_id are all uuid columns. The previous $1::text NULL
    guard caused pg to infer the bound param as text, then the org_id = $1 comparison
    threw 'operator does not exist: uuid = text' for any non-null orgId.
    
    /api/pipeline/stats was 500-ing for SDCC staff. /api/settings would have done the
    same had the .catch() not been swallowing it silently into a 0-row default.
    
    Cast both occurrences as ::uuid so the null-guard and the comparison agree.
---
 app/api/pipeline/stats/route.ts | 10 +++++-----
 app/api/settings/route.ts       | 10 +++++-----
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/app/api/pipeline/stats/route.ts b/app/api/pipeline/stats/route.ts
index 71ef93f..5bd6aba 100644
--- a/app/api/pipeline/stats/route.ts
+++ b/app/api/pipeline/stats/route.ts
@@ -25,7 +25,7 @@ export async function GET(request: NextRequest) {
         query(
           `SELECT status, COUNT(*)::int AS count
            FROM petition_pipeline
-           WHERE ($1::text IS NULL OR org_id = $1)
+           WHERE ($1::uuid IS NULL OR org_id = $1::uuid)
            GROUP BY status
            ORDER BY count DESC`,
           [orgId]
@@ -33,21 +33,21 @@ export async function GET(request: NextRequest) {
         query(
           `SELECT platform, COUNT(*)::int AS count
            FROM petition_pipeline
-           WHERE ($1::text IS NULL OR org_id = $1)
+           WHERE ($1::uuid IS NULL OR org_id = $1::uuid)
            GROUP BY platform
            ORDER BY count DESC`,
           [orgId]
         ),
         query(
           `SELECT COUNT(*)::int AS total FROM petition_pipeline
-           WHERE ($1::text IS NULL OR org_id = $1)`,
+           WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`,
           [orgId]
         ),
         query(
           `SELECT COUNT(*)::int AS count
            FROM petition_pipeline
            WHERE created_at >= NOW() - INTERVAL '24 hours'
-             AND ($1::text IS NULL OR org_id = $1)`,
+             AND ($1::uuid IS NULL OR org_id = $1::uuid)`,
           [orgId]
         ),
         query(
@@ -56,7 +56,7 @@ export async function GET(request: NextRequest) {
              COUNT(*)::int AS ai_generated
            FROM petition_pipeline
            WHERE ai_confidence IS NOT NULL
-             AND ($1::text IS NULL OR org_id = $1)`,
+             AND ($1::uuid IS NULL OR org_id = $1::uuid)`,
           [orgId]
         ),
       ]);
diff --git a/app/api/settings/route.ts b/app/api/settings/route.ts
index 929ba6e..574047e 100644
--- a/app/api/settings/route.ts
+++ b/app/api/settings/route.ts
@@ -22,11 +22,11 @@ export async function GET(request: NextRequest) {
     // Also get data stats (scoped by org where possible)
     const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;
     const statsQueries = await Promise.all([
-      query(`SELECT COUNT(*)::int AS count FROM drafts WHERE ($1::text IS NULL OR org_id = $1)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
-      query(`SELECT COUNT(*)::int AS count FROM news_items WHERE ($1::text IS NULL OR org_id = $1)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
-      query(`SELECT COUNT(*)::int AS count FROM collaborations WHERE ($1::text IS NULL OR org_id = $1)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
-      query(`SELECT COUNT(*)::int AS count FROM partners WHERE ($1::text IS NULL OR org_id = $1)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
-      query(`SELECT COUNT(*)::int AS count FROM old_leads WHERE ($1::text IS NULL OR org_id = $1)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
+      query(`SELECT COUNT(*)::int AS count FROM drafts WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
+      query(`SELECT COUNT(*)::int AS count FROM news_items WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
+      query(`SELECT COUNT(*)::int AS count FROM collaborations WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
+      query(`SELECT COUNT(*)::int AS count FROM partners WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
+      query(`SELECT COUNT(*)::int AS count FROM old_leads WHERE ($1::uuid IS NULL OR org_id = $1::uuid)`, [orgId]).catch(() => ({ rows: [{ count: 0 }] })),
     ]);
 
     settings.data_stats = {

← c0eed7c docs: morning wakeup brief — DB password fix + verification  ·  back to Norma  ·  petition create: debounce double-submit via useRef guard + 3 9619a2a →