name: supabase-upsert-vs-insert
description: Fix Supabase JS client insert() silently returning false by using upsert() instead
Supabase JS Client: insert() returning false silently — use upsert() instead
Problem
When calling supabase.from('tasks').insert({...}) from the browser (TanStack Start / React SPA), the operation sometimes returns { data: null, error: null } without any actual database write occurring. The onSave callback receives false but the error message is empty/misleading.
Symptoms:
- - Task creation fails with no visible error or misleading error
- -
console.errorshows structured error info but toast shows generic message - - REST API inserts work fine but browser client inserts silently fail
- - No RLS policy issues (RLS is off on the table)
- -
onConflict: 'id'— tells Postgres which column to check - -
ignoreDuplicates: true— if record exists, do nothing (no error thrown) - - When insert is called from a UI callback (onSave, form submit)
- - When the record has a pre-generated UUID (not auto-generated)
- - When you need reliable error capture from the Supabase client
- - When insert appears to succeed but data doesn't appear in the table
- -
addTaskinAppDataContext.tsx— uses this pattern for task creation - - Changed from
inserttoupserton 2026-05-22 to fix silent failures
Root Cause
The Supabase JS client's postgrest-js library uses a Prefer: return=representation header by default for inserts. In certain edge conditions (possibly related to batch response parsing in the client), the response is received but not correctly parsed, resulting in null data with no error.
Solution
Use .upsert() instead of .insert() with explicit conflict handling:
`typescript
const { error } = await supabase.from('table_name').upsert(
{
id: record.id,
field1: record.field1,
field2: record.field2,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
} as any,
{ onConflict: 'id', ignoreDuplicates: true }
);
`
Key options:
Also wrap in try/catch to handle network errors:
`typescript
let error = null;
try {
const { error: err } = await supabase.from('tasks').upsert({...});
error = err;
} catch (e: any) {
error = { message: e?.message || String(e), code: 'NETWORK_ERROR' };
}
`