mckaywrigley/chatbot-ui · error · Error
error.message
Error message
error.message
What it means
Supabase (supabase-js) insert on the 'messages' table returned a PostgREST error instead of a created row. The `.single()` call expects exactly one row back; the error object carries the PostgREST message (constraint violation, RLS denial, missing column, etc.). This is a database-level rejection of the INSERT, not a network failure.
Source
Thrown at db/messages.ts:39
.select("*")
.eq("chat_id", chatId)
if (!messages) {
throw new Error("Messages not found")
}
return messages
}
export const createMessage = async (message: TablesInsert<"messages">) => {
const { data: createdMessage, error } = await supabase
.from("messages")
.insert([message])
.select("*")
.single()
if (error) {
throw new Error(error.message)
}
return createdMessage
}
export const createMessages = async (messages: TablesInsert<"messages">[]) => {
const { data: createdMessages, error } = await supabase
.from("messages")
.insert(messages)
.select("*")
if (error) {
throw new Error(error.message)
}
return createdMessages
}
View on GitHub (pinned to 81328b61d2)
Solutions
- Inspect error.message and error.code — PostgREST error codes like 23503 (foreign_key_violation), 23505 (unique_violation), or 42501 (RLS) pinpoint the cause
- Verify all required columns in the message payload (check NOT NULL columns and FK targets in the Supabase dashboard Table Editor)
- If RLS is enabled on 'messages', add an INSERT policy (e.g. WITH CHECK (auth.uid() = user_id)) or use the service-role client for trusted server-side inserts
- Confirm snake_case column names in the payload match the actual schema
Example fix
// before
const { data: createdMessage, error } = await supabase
.from("messages")
.insert([message])
.select("*")
.single()
if (error) {
throw new Error(error.message)
}
// after — surface code + details for diagnosis
if (error) {
throw new Error(`createMessage failed [${error.code}]: ${error.message}`, { cause: error })
} Defensive patterns
Strategy: validation
Validate before calling
// Verify FK targets exist before inserting
const { data: chat } = await supabase.from("chats").select("id").eq("id", message.chat_id).maybeSingle()
if (!chat) throw new Error(`chat ${message.chat_id} does not exist`)
const required = ["chat_id", "content"] // adjust to your NOT NULL columns
for (const k of required) {
if (message[k] === undefined || message[k] === null) throw new Error(`missing field: ${k}`)
} Type guard
const isMessageInsert = (m: unknown): m is TablesInsert<"messages"> => typeof m === "object" && m !== null && "chat_id" in m && "content" in m
Try / catch
try {
return await createMessage(message)
} catch (e) {
const msg = e instanceof Error ? e.message : String(e)
if (msg.includes("duplicate key")) throw new ConflictError(msg)
if (msg.includes("violates foreign key")) throw new BadRequestError(msg)
throw e
} Prevention
- Strip undefined keys from payloads before insert
- Keep a runtime schema mirror (zod) of NOT NULL columns and validate payloads
- Run server-side writes with the service-role client or maintain explicit RLS INSERT policies
When it happens
Trigger: Calling createMessage with a payload that violates a NOT NULL or FOREIGN KEY constraint on 'messages' (e.g. missing user_id or a non-existent chat/thread id), inserting a value exceeding a column's length, or hitting a UNIQUE constraint; also occurs when row-level security policies block the insert for the current anon/auth client.
Common situations: RLS enabled on 'messages' without an INSERT policy so anonymous/server-client inserts are silently rejected; passing camelCase keys instead of snake_case column names; environment mismatch where the local schema lacks columns present in the production schema; JWT of a user who doesn't own the referenced chat.
Related errors
AI-assisted analysis of mckaywrigley/chatbot-ui@81328b61d2 (2026-08-27).
Data as JSON: /api/errors/8e0a2cb4f6c64374.
Report an issue: GitHub.