Postgres Node returns empty data for existing ID (Supabase + Telegram Bot)

Workflow Overview:

I am building a stateless Telegram Quiz bot.

  • Trigger: Receives callback_query or message.

  • Logic: Get User (DB) $\rightarrow$ Fetch Questions $\rightarrow$ Calculate Score (Code) $\rightarrow$ Save State (DB) $\rightarrow$ Send Next Question.

Design Rationale:

We switched from “Wait” nodes to a Stateless (Database-driven) approach because Telegram button clicks trigger new executions, causing the Wait node context to be lost.

The Failure:

The bot gets stuck in a loop (repeating Question 2) because the Postgres - Get User node returns an empty item [{}] even though the user definitely exists in the database.

Because it returns empty, the Logic node assumes a “New User,” resets the sequence to 1, and the user never progresses.

What We Have Tried (Troubleshooting):

  1. Verified Data Exists: Checked Supabase directly; User 648416370 exists with current_sequence: 2.

  2. Checked RLS: Row Level Security is OFF in Supabase.

  3. Data Types: The DB column is int8 (BigInt). We tried casting the query to text to avoid type mismatches: WHERE telegram_id::text = $1::text.

  4. Node Settings: Set “Always Output Data” to true (returns an empty item instead of null).

  5. Trigger: Verified the Trigger is correctly receiving the callback_query and ID.

Current Status:

The node executes successfully but returns 0 results for a known ID. Suspecting a connection/environment mismatch or a specific n8n<->Supabase parameter parsing issue.

after an hour with gemini
Topic: Solved - Postgres Node returns empty data for existing Telegram ID (BigInt/Text Mismatch)

The Issue: I was building a Telegram bot where n8n needed to check if a user existed (SELECT * FROM users WHERE id = $1). Even though the user existed in Supabase (verified via SQL), the n8n Postgres node kept returning 0 items (Empty).

  • Root Cause: A persistent mismatch between Telegram IDs (which are huge numbers) and Postgres Int8 columns. N8n/JavaScript handles these large numbers inconsistently (sometimes as Numbers, sometimes Strings), causing the WHERE clause to fail silently (e.g., 123 !== "123").

The Solution: The “Atomic Upsert” Pattern Instead of trying to debug the SELECT node’s type matching, we replaced the “Get User” node with an “Initialize User” node using a single SQL INSERT command with RETURNING *.

The Magic Query:

SQL

INSERT INTO "HP_Users" (telegram_id)
VALUES ('{{ ($json.callback_query ? $json.callback_query.from.id : $json.message.chat.id).toString() }}')
ON CONFLICT (telegram_id)
DO UPDATE SET last_question_ts = NOW()
RETURNING *;

Why this works:

  1. Bypasses the Search: We don’t rely on a WHERE clause matching the ID perfectly.

  2. Handles Both Cases:

    • New User: It inserts them.

    • Existing User: It hits a conflict and harmlessly updates a timestamp.

  3. Guaranteed Return: The RETURNING * command forces the database to send back the user’s current state (Score/Sequence) in the same step.

  4. No Logic Branching: We deleted the “If User Exists” logic node entirely because this one SQL node handles both creation and retrieval.

1 Like

this solved it for me, but if there’s a better solution?
or no one else encountered this.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.