Dokploy/dokploy · error · TRPCError

INTERNAL_SERVER_ERROR

INTERNAL_SERVER_ERROR

Error message

Error removing the node

What it means

INTERNAL_SERVER_ERROR wrapper around the cluster node-removal mutation. Any failure in the underlying docker/swarm node removal (node not found, node not drained, Docker API error) is re-thrown with this generic message and the original error as `cause`.

Source

Thrown at apps/dokploy/server/api/routers/cluster.ts:73

				const drainCommand = `docker node update --availability drain ${quote([input.nodeId])}`;
				const removeCommand = `docker node rm ${quote([input.nodeId])} --force`;

				if (input.serverId) {
					await execAsyncRemote(input.serverId, drainCommand);
					await execAsyncRemote(input.serverId, removeCommand);
				} else {
					await execAsync(drainCommand);
					await execAsync(removeCommand);
				}
				await audit(ctx, {
					action: "delete",
					resourceType: "cluster",
					resourceId: input.nodeId,
					resourceName: input.nodeId,
				});
				return true;
			} catch (error) {
				throw new TRPCError({
					code: "INTERNAL_SERVER_ERROR",
					message: "Error removing the node",
					cause: error,
				});
			}
		}),

	addWorker: withPermission("server", "create")
		.input(
			z.object({
				serverId: z.string().optional(),
			}),
		)
		.query(async ({ input, ctx }) => {
			if (input.serverId) {
				const targetServer = await findServerById(input.serverId);
				if (targetServer.organizationId !== ctx.session.activeOrganizationId) {
					throw new TRPCError({

View on GitHub (pinned to 546686ea35)

Solutions

  1. Inspect `error.cause` for the Docker API message
  2. Drain the node (`docker node update --availability drain <id>`) before removal
  3. Verify the target server and its Docker daemon are reachable
  4. If the node already left, refresh the node list — it may have succeeded despite the error

Example fix

# before
docker node rm <nodeId>  # fails if active

# after
docker node update --availability drain <nodeId>
docker node rm <nodeId>
Defensive patterns

Strategy: try-catch

Validate before calling

// Drain first via docker API before removing
await docker.getNode(nodeId).update({ Availability: 'drain' });

Try / catch

try {
  await trpc.cluster.removeNode.mutate({ nodeId });
} catch (e: any) {
  const c = e?.cause?.message ?? '';
  if (/not drained/i.test(c)) { /* drain then retry once */ }
  else if (/node not found/i.test(c)) { /* refresh list, treat as success */ }
  else throw e;
}

Prevention

When it happens

Trigger: Removing a swarm node that is still active (not drained), a node that already left the swarm, Docker daemon unreachable on the target server, or RPC timeout to the remote Docker socket.

Common situations: Trying to delete a node without draining it first; remote server offline; Docker socket permission issues; race with another operator removing the node simultaneously.

Related errors


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