{"record":{"id":"e08e7c1fb6e4d9e3","repo":"n8n-io/n8n","slug":"supabase-upsert-failed-error-message","errorCode":null,"errorMessage":"Supabase upsert failed: ${error.message}","messagePattern":"Supabase upsert failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/vector-stores/supabase.ts","lineNumber":122,"sourceCode":"\t\tsuper(name, options);\n\t\tthis.tableName = options.tableName;\n\t\tthis.queryName = options.queryName ?? 'match_documents';\n\t}\n\n\tasync upsert(records: VectorRecord[]): Promise<void> {\n\t\tif (records.length === 0) return;\n\n\t\tconst client = await this.getClient();\n\t\tconst { error } = await client.from(this.tableName).upsert(\n\t\t\trecords.map((record) => ({\n\t\t\t\tid: record.id,\n\t\t\t\tcontent: record.content,\n\t\t\t\tmetadata: record.metadata,\n\t\t\t\tembedding: record.vector,\n\t\t\t})),\n\t\t\t{ onConflict: 'id' },\n\t\t);\n\t\tif (error) throw new Error(`Supabase upsert failed: ${error.message}`);\n\t}\n\n\tasync query(\n\t\tvector: number[],\n\t\topts: { topK: number; filter?: VectorFilter },\n\t): Promise<VectorQueryResult[]> {\n\t\tconst client = await this.getClient();\n\t\tconst rpcCall = client.rpc<string, MatchDocumentsFn>(this.queryName, {\n\t\t\tquery_embedding: vector,\n\t\t});\n\t\tconst filtered =\n\t\t\topts.filter && opts.filter.conditions.length > 0\n\t\t\t\t? applySupabaseFilter(rpcCall, opts.filter)\n\t\t\t\t: rpcCall;\n\n\t\tconst { data, error } = await filtered.limit(opts.topK);\n\t\tif (error) throw new Error(`Supabase query failed: ${error.message}`);\n","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/vector-stores/supabase.ts#L104-L140","documentation":"Thrown by SupabaseVectorStore.upsert when the PostgREST upsert call returns a non-null `error` object. The underlying message from the Supabase/PostgREST client is appended, so the root cause is typically RLS denial, a missing table, a schema/column mismatch, a unique-constraint violation not handled by onConflict, or a network/auth problem. This is a runtime/backend error, not input validation.","triggerScenarios":"RLS policy blocking inserts/updates for the api key's role; tableName pointing at a non-existent table; embedding column missing or wrong type; metadata column not jsonb; onConflict:'id' failing because `id` is not the primary key; service role key expired/wrong; Supabase project paused/unreachable.","commonSituations":"Using the anon key instead of the service role key for writes; RLS enabled without a permissive policy for the backend role; migrating the table schema and forgetting the embedding column; Supabase free-tier project auto-pausing; network egress blocked.","solutions":["Read the appended PostgREST message first — it names the concrete failure (e.g. 'relation does not exist', 'permission denied', 'violates unique constraint').","For RLS, use the service role key for trusted backend writes, or add an INSERT/UPDATE policy for the table.","Verify the table exists with columns id, content, metadata (jsonb), embedding (vector(n)) matching the embedded dimension.","For transient/network errors, retry with backoff."],"exampleFix":"// before — anon key blocked by RLS\nnew SupabaseVectorStore('docs', {\n  url, apiKey: process.env.SUPABASE_ANON_KEY, tableName: 'docs',\n});\n\n// after — service role key for trusted backend writes\nnew SupabaseVectorStore('docs', {\n  url, apiKey: process.env.SUPABASE_SERVICE_ROLE_KEY, tableName: 'docs',\n});","handlingStrategy":"retry","validationCode":"function assertSupabaseWritable(opts: { url: string; apiKey: string; tableName: string }): void {\n  if (!/^https?:\\/\\/.+/.test(opts.url)) throw new Error('Supabase url missing/invalid');\n  if (!opts.apiKey) throw new Error('Supabase apiKey missing (use service role key for writes)');\n  if (!opts.tableName) throw new Error('Supabase tableName missing');\n}\n\nassertSupabaseWritable(opts);\nnew SupabaseVectorStore('docs', opts);","typeGuard":"function looksLikeServiceRoleKey(key: string): boolean {\n  // Supabase service role keys are long JWTs; anon keys are too, so this is a heuristic —\n  // the real test is whether writes succeed (RLS rejects anon-role writes).\n  return /^eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/.test(key);\n}","tryCatchPattern":"async function upsertWithRetry(store: SupabaseVectorStore, records: VectorRecord[], attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      await store.upsert(records);\n      return;\n    } catch (err) {\n      const msg = err instanceof Error ? err.message : '';\n      const transient = /network|timeout|fetch|ECONN|503|504|paused/i.test(msg);\n      if (!transient || i === attempts - 1) throw err;\n      await new Promise((r) => setTimeout(r, 2 ** i * 200));\n    }\n  }\n}","preventionTips":["Use the service role key for trusted backend writes to bypass RLS; never rely on the anon key for writes.","Define an INSERT/UPDATE RLS policy if you must use a non-service role.","Verify the table schema (id, content, metadata jsonb, embedding vector(n)) before deploying.","Retry only transient/network errors; surface RLS/schema errors to the operator immediately."],"tags":["supabase","database","network","rls","vector-store"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}