Supabase Vector Store insert fails on custom kb_chunks schema (source_id null) in n8n 2.17.7

I’m trying to ingest policy docs into Supabase using @n8n/n8n-nodes-langchain.vectorStoreSupabase (Insert Documents mode), but inserts fail on my custom kb_chunks table.

My table schema expects top-level columns like:

  • source_id text not null

  • source_type text not null

  • chunk_index integer not null

  • content text not null

  • metadata jsonb

  • embedding vector(3072)

I’m using Default Data Loader + Recursive Character Text Splitter + Embeddings Google Gemini feeding into Insert Into Supabase Vector Store.

I can see source_id in upstream data and in loader metadata, but Supabase still throws null constraint error on source_id.

I want to know the correct n8n pattern for this table shape:

  • Should source_id/source_type/chunk_index be top-level DB columns with this node?

  • Or does the vector store node only reliably write content/embedding/metadata and I should derive extra columns via trigger/view?

  • Best practice for n8n v2.17.7?


What is the error message (if any)?

Error inserting: null value in column "source_id" of relation "kb_chunks" violates not-null constraint 400 Bad Request


workflow

Minimal graph:

  • Manual Trigger

  • Prepare Knowledge Documents (Code node, returns array of docs with source_id, source_type, title, content)

  • Recursive Character Text Splitter

  • Default Data Loader

    • jsonData = {{ $json.content }}

    • metadata values:

      • source_id = {{ $json.source_id }}

      • source_type = {{ $json.source_type }}

      • title = {{ $json.title }}

  • Embeddings Google Gemini (models/gemini-embedding-2)

  • Insert Into Supabase Vector Store

    • mode: insert

    • table: kb_chunks

    • queryName: match_documents

Connection intent:

  • Splitter → Data Loader (ai_textSplitter)

  • Data Loader → Vector Store (ai_document)

  • Embeddings → Vector Store (ai_embedding)

I previously had a direct main connection from Prepare Knowledge Documents to Insert Into Supabase Vector Store; removing/changing wiring still gives the same source_id null issue.


output returned by the last node

Last node (Insert Into Supabase Vector Store) output is error only:

null value in column "source_id" of relation "kb_chunks" violates not-null constraint

No successful inserted rows.


Information on n8n setup

  • n8n version: 2.17.7

  • Database: (please set this to your actual n8n DB, mine is self-hosted; Supabase is used as vector store target)

  • n8n EXECUTIONS_PROCESS: (not sure)

  • Running n8n via: self-hosted local instance (exposed with ngrok when needed)

  • Operating system: macOS

@Divine_Jude the supabase vector store node only ever writes three columns — content, embedding, and metadata (jsonb). it completely ignores any other top-level columns on your table, so source_id will always be null no matter what you put in the loader metadata. easiest fix is a Postgres BEFORE INSERT trigger that pulls those fields out of the metadata jsonb automatically.

run this on your supabase SQL editor first:

create or replace function kb_chunks_fill_columns()
returns trigger as $$
begin
  NEW.source_id := NEW.metadata->>'source_id';
  NEW.source_type := NEW.metadata->>'source_type';
  NEW.chunk_index := (NEW.metadata->>'chunk_index')::integer;
  return NEW;
end;
$$ language plpgsql;

create trigger kb_chunks_before_ins
  before insert on kb_chunks
  for each row execute function kb_chunks_fill_columns();

then this workflow handles the ingest side correctly:

plug in your supabase and gemini creds, swap the embedding model to gemini-embedding-2 if you want 3072 dims. the trigger does the heavy lifting — your metadata fields land in the jsonb column and Postgres copies them to the top-level columns before the row commits.

good day @Divine_Jude
I’d keep the ingestion table in the format expected by the node and create a separate view or table for querying with source_id, source_type, and chunk_index as business fields. That way, you avoid coupling the vector store insert process to required columns the node doesn’t populate directly. In production, this is usually easier to maintain than relying on every ingestion path to map custom columns correctly.

Thanks @achamm for your input it resolved my query. I turly appreciate.

Thanks for your input @tamy.santos Resolved!