{"record":{"id":"a61788d7325c8569","repo":"Dokploy/dokploy","slug":"internal-server-error-a61788","errorCode":"INTERNAL_SERVER_ERROR","errorMessage":"Error removing the node","messagePattern":"Error removing the node","errorType":"exception","errorClass":"TRPCError","httpStatus":500,"severity":"error","filePath":"apps/dokploy/server/api/routers/cluster.ts","lineNumber":73,"sourceCode":"\t\t\t\tconst drainCommand = `docker node update --availability drain ${quote([input.nodeId])}`;\n\t\t\t\tconst removeCommand = `docker node rm ${quote([input.nodeId])} --force`;\n\n\t\t\t\tif (input.serverId) {\n\t\t\t\t\tawait execAsyncRemote(input.serverId, drainCommand);\n\t\t\t\t\tawait execAsyncRemote(input.serverId, removeCommand);\n\t\t\t\t} else {\n\t\t\t\t\tawait execAsync(drainCommand);\n\t\t\t\t\tawait execAsync(removeCommand);\n\t\t\t\t}\n\t\t\t\tawait audit(ctx, {\n\t\t\t\t\taction: \"delete\",\n\t\t\t\t\tresourceType: \"cluster\",\n\t\t\t\t\tresourceId: input.nodeId,\n\t\t\t\t\tresourceName: input.nodeId,\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t} catch (error) {\n\t\t\t\tthrow new TRPCError({\n\t\t\t\t\tcode: \"INTERNAL_SERVER_ERROR\",\n\t\t\t\t\tmessage: \"Error removing the node\",\n\t\t\t\t\tcause: error,\n\t\t\t\t});\n\t\t\t}\n\t\t}),\n\n\taddWorker: withPermission(\"server\", \"create\")\n\t\t.input(\n\t\t\tz.object({\n\t\t\t\tserverId: z.string().optional(),\n\t\t\t}),\n\t\t)\n\t\t.query(async ({ input, ctx }) => {\n\t\t\tif (input.serverId) {\n\t\t\t\tconst targetServer = await findServerById(input.serverId);\n\t\t\t\tif (targetServer.organizationId !== ctx.session.activeOrganizationId) {\n\t\t\t\t\tthrow new TRPCError({","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/Dokploy/dokploy/blob/546686ea3587f12ec5652217dedd9f7960fb6d15/apps/dokploy/server/api/routers/cluster.ts#L55-L91","documentation":"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`.","triggerScenarios":"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.","commonSituations":"Trying to delete a node without draining it first; remote server offline; Docker socket permission issues; race with another operator removing the node simultaneously.","solutions":["Inspect `error.cause` for the Docker API message","Drain the node (`docker node update --availability drain <id>`) before removal","Verify the target server and its Docker daemon are reachable","If the node already left, refresh the node list — it may have succeeded despite the error"],"exampleFix":"# before\ndocker node rm <nodeId>  # fails if active\n\n# after\ndocker node update --availability drain <nodeId>\ndocker node rm <nodeId>","handlingStrategy":"try-catch","validationCode":"// Drain first via docker API before removing\nawait docker.getNode(nodeId).update({ Availability: 'drain' });","typeGuard":null,"tryCatchPattern":"try {\n  await trpc.cluster.removeNode.mutate({ nodeId });\n} catch (e: any) {\n  const c = e?.cause?.message ?? '';\n  if (/not drained/i.test(c)) { /* drain then retry once */ }\n  else if (/node not found/i.test(c)) { /* refresh list, treat as success */ }\n  else throw e;\n}","preventionTips":["Always drain swarm nodes before removal","Check remote Docker connectivity before cluster operations","Refresh node lists after failures to detect already-removed nodes"],"tags":["dokploy","trpc","cluster","swarm","docker-node","catch-all"],"backgroundTag":"docker-swarm-node-removal-failed","analyzedSha":"546686ea3587f12ec5652217dedd9f7960fb6d15","analyzedAt":"2026-08-27T05:18:58.095Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}