langfuse/langfuse · warning · TRPCError

CONFLICT

CONFLICT

Error message

error.message

What it means

CONFLICT TRPCError from the dashboardWidgets delete mutation (dashboardWidgets.ts:222). Deleting a widget that is still referenced by one or more dashboards causes the service to raise LangfuseConflictError, which is translated to a tRPC CONFLICT (HTTP 409) carrying the underlying error.message (e.g. 'Widget is still referenced in dashboards').

Source

Thrown at web/src/server/api/routers/dashboardWidgets.ts:222

    )
    .mutation(async ({ input, ctx }) => {
      throwIfNoProjectAccess({
        session: ctx.session,
        projectId: input.projectId,
        scope: "dashboards:CUD",
      });

      try {
        // Delete the widget using the DashboardService
        await DashboardService.deleteWidget(input.widgetId, input.projectId);

        return {
          success: true,
        };
      } catch (error) {
        // If the widget is still referenced in dashboards, throw a CONFLICT error
        if (error instanceof LangfuseConflictError) {
          throw new TRPCError({
            code: "CONFLICT",
            message: error.message,
          });
        }
        throw new TRPCError({
          code: "INTERNAL_SERVER_ERROR",
          message: (error as Error)?.message,
        });
      }
    }),
});

View on GitHub (pinned to 59d92c7cf3)

Solutions

  1. Remove the widget from every dashboard that references it (edit each dashboard's widget list) before deleting it
  2. Use the API response to identify which dashboards still reference the widget, then unlink them
  3. If the UI should allow force-delete, first detach references in one transaction then delete the widget
  4. Catch CONFLICT client-side and prompt the user to unlink dashboards

Example fix

// before
await api.dashboardWidgets.delete.mutate({ projectId, widgetId });

// after
try {
  await api.dashboardWidgets.delete.mutate({ projectId, widgetId });
} catch (e) {
  if (e?.data?.code === 'CONFLICT') {
    showUnlinkDashboardsPrompt(e.message);
    return;
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const dashboards = await api.dashboards.list.query({ projectId });
const referencing = dashboards.filter((d) => d.widgets.some((w) => w.id === widgetId));
if (referencing.length > 0) throw new Error('unlink dashboards first');

Type guard

null

Try / catch

try {
  await api.dashboardWidgets.delete.mutate({ projectId, widgetId });
} catch (e) {
  if (e?.data?.code === 'CONFLICT') return promptUnlink(e.message);
  throw e;
}

Prevention

When it happens

Trigger: Calling the widget delete mutation while at least one dashboard row still includes this widget in its widgetIds/references; typical when a widget is shared across multiple dashboards or a dashboard was created from a template that cloned the reference.

Common situations: Shared KPI widgets reused on several dashboards; importing template dashboards that reference existing widgets; stale dashboard rows created before cascade cleanup was added.

Related errors


AI-assisted analysis of langfuse/langfuse@59d92c7cf3 (2026-08-27). Data as JSON: /api/errors/5b72aca2b88a755f. Report an issue: GitHub.