Dokploy/dokploy · error · TRPCError

BAD_REQUEST

BAD_REQUEST

Error message

externalGRPCPort cannot be set when sqldNode is 'replica'

What it means

Libsql replicas sync from a primary and must not expose their own external gRPC port. The update procedure rejects the combination sqldNode === 'replica' with a non-null externalGRPCPort as BAD_REQUEST — a domain business rule, not a system failure.

Source

Thrown at apps/dokploy/server/api/routers/libsql.ts:184

			await audit(ctx, {
				action: "stop",
				resourceType: "service",
				resourceId: libsql.libsqlId,
				resourceName: libsql.appName,
			});
			return libsql;
		}),
	saveExternalPorts: protectedProcedure
		.input(apiSaveExternalPortsLibsql)
		.mutation(async ({ input, ctx }) => {
			await checkServicePermissionAndAccess(ctx, input.libsqlId, {
				service: ["create"],
			});
			const libsql = await findLibsqlById(input.libsqlId);

			if (libsql.sqldNode === "replica" && input.externalGRPCPort !== null) {
				throw new TRPCError({
					code: "BAD_REQUEST",
					message: "externalGRPCPort cannot be set when sqldNode is 'replica'",
				});
			}

			const portsToCheck = [
				{
					port: input.externalPort,
					name: "externalPort",
					current: libsql.externalPort,
				},
				{
					port: input.externalGRPCPort,
					name: "externalGRPCPort",
					current: libsql.externalGRPCPort,
				},
				{
					port: input.externalAdminPort,

View on GitHub (pinned to 546686ea35)

Solutions

  1. Set externalGRPCPort to null (or omit it) when choosing sqldNode 'replica'
  2. If you need the port, keep/promote the node to primary instead
  3. Split into two updates only if the UI requires: first clear the port, then change role

Example fix

// before
await api.libsql.update.mutate({ libsqlId, sqldNode: "replica", externalGRPCPort: 5001 });
// after
await api.libsql.update.mutate({ libsqlId, sqldNode: "replica", externalGRPCPort: null });
Defensive patterns

Strategy: validation

Validate before calling

if (sqldNode === "replica") input.externalGRPCPort = null;

Type guard

const isValidRolePortCombo = (node: string, port: number | null): boolean =>
  node !== "replica" || port === null;

Prevention

When it happens

Trigger: Editing a database and switching its role to 'replica' while leaving an externalGRPCPort set, or explicitly sending a port for a replica.

Common situations: Migrating a standalone Libsql into a replica topology without clearing the previously assigned gRPC port in the same update.

Understand the failure class

Background: BAD_REQUEST error code: request rejected as invalid (HTTP 400) - causes and fixes across libraries — this error's family across 8 libraries.

Related errors


AI-assisted analysis of Dokploy/dokploy@546686ea35 (2026-08-27). Data as JSON: /api/errors/6d6ef10e1895e894. Report an issue: GitHub.