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

  1. Inspect error.message and error.code — PostgREST error codes like 23503 (foreign_key_violation), 23505 (unique_violation), or 42501 (RLS) pinpoint the cause
  2. Verify all required columns in the message payload (check NOT NULL columns and FK targets in the Supabase dashboard Table Editor)
  3. 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
  4. 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

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.