BloopAI/vibe-kanban · error

Failed to create ${mutation.name} (or server-provided messag

Error message

Failed to create ${mutation.name} (or server-provided message via parseResponseError)

What it means

The onInsert mutation handler POSTs each inserted row to mutation.url and, on a non-ok response, throws an Error whose message comes from parseResponseError — the server's `message`/`error` field, or the fallback 'Failed to create <name>'. This aborts the optimistic transaction, so Electric rolls back the local insert.

Source

Thrown at packages/web-core/src/shared/lib/electric/collections.ts:643

) {
  return {
    onInsert: async ({
      transaction,
    }: MutationFnParams): Promise<{ txid: number[] } | void> => {
      const txids = await Promise.all(
        transaction.mutations.map(async (mutationItem) => {
          const data = mutationItem.modified as Record<string, unknown>;
          const response = await makeRequest(mutation.url, {
            method: 'POST',
            body: JSON.stringify(data),
          });

          if (!response.ok) {
            const message = await parseResponseError(
              response,
              `Failed to create ${mutation.name}`
            );
            throw new Error(message);
          }

          const result = (await response.json()) as { txid: number };
          return result.txid;
        })
      );

      maybeRefreshFallbackAfterMutation(sourceKey);

      if (isSourceFallbackLocked(sourceKey)) {
        return;
      }

      return { txid: txids };
    },

    onUpdate: async ({
      transaction,

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Read the thrown message — it usually contains the server's validation reason (parseResponseError surfaced it).
  2. Validate the row against server schema before optimistic insert (zod schema shared via shared/schemas).
  3. Handle 401 by refreshing auth and retrying the transaction.
  4. Confirm mutation.url is the correct create endpoint and accepts the exact JSON body being sent.

Example fix

// before
await collection.insert(data); // throws on server rejection
// after
const parsed = taskCreateSchema.safeParse(data);
if (!parsed.success) { showError(parsed.error); return; }
await collection.insert(parsed.data);
Defensive patterns

Strategy: try-catch

Validate before calling

const parsed = createSchema.safeParse(data);
if (!parsed.success) {
  showError(parsed.error.message);
  return;
}

Type guard

function isCreateFailure(msg: string): boolean {
  return msg.startsWith('Failed to create ');
}

Try / catch

try {
  await collection.insert(data);
} catch (e) {
  if (isCreateFailure(e.message)) {
    showToast(e.message); // server-provided reason surfaced by parseResponseError
    return; // optimistic insert is rolled back automatically
  }
  throw e;
}

Prevention

When it happens

Trigger: POST to the mutation endpoint fails validation (400/422), user lacks permission (403), session expired (401), the record conflicts (409), or the route returns 500; also thrown per-item inside Promise.all so any one failing insert rejects the whole transaction.

Common situations: Required field missing from the new row per server-side validation; optimistic UI allowed an edit the server rejects; token expired mid-session; server enforces uniqueness the client didn't check; mutation.url points at a stale/wrong API path after refactor.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/681ba0d343c7f14f. Report an issue: GitHub.