Dokploy/dokploy · error · TRPCError

BAD_REQUEST

BAD_REQUEST

Error message

Error adding environment variables

What it means

BAD_REQUEST thrown when updateCompose returns falsy while saving environment variables (env + optional createEnvFile) for a compose. The service returns undefined/null when the update affects no rows — typically because the composeId doesn't exist.

Source

Thrown at apps/dokploy/server/api/routers/compose.ts:219

				resourceType: "compose",
				resourceId: input.composeId,
				resourceName: updated?.name,
			});
			return updated;
		}),
	saveEnvironment: protectedProcedure
		.input(apiSaveEnvironmentVariablesCompose)
		.mutation(async ({ input, ctx }) => {
			await checkServicePermissionAndAccess(ctx, input.composeId, {
				envVars: ["write"],
			});
			const updated = await updateCompose(input.composeId, {
				env: input.env,
				createEnvFile: input.createEnvFile,
			});

			if (!updated) {
				throw new TRPCError({
					code: "BAD_REQUEST",
					message: "Error adding environment variables",
				});
			}

			await audit(ctx, {
				action: "update",
				resourceType: "compose",
				resourceId: input.composeId,
				resourceName: updated?.name,
			});
			return true;
		}),
	delete: protectedProcedure
		.input(apiDeleteCompose)
		.mutation(async ({ input, ctx }) => {
			await checkServiceAccess(ctx, input.composeId, "delete");
			const composeResult = await findComposeById(input.composeId);

View on GitHub (pinned to 546686ea35)

Solutions

  1. Confirm the compose still exists (compose.one) before saving env vars
  2. Disable the form during deletion flows; refetch after mutations
  3. Check for concurrent processes deleting the compose

Example fix

// before
await trpc.compose.saveEnv.mutate({ composeId, env, createEnvFile: true });

// after
const exists = await trpc.compose.one.query({ composeId });
if (!exists) throw new Error('Compose no longer exists');
await trpc.compose.saveEnv.mutate({ composeId, env, createEnvFile: true });
Defensive patterns

Strategy: type-guard

Validate before calling

const compose = await trpc.compose.one.query({ composeId }).catch(() => null);
if (!compose) throw new Error('Compose missing — cannot save env vars');

Type guard

async function composeExists(id: string): Promise<boolean> {
  try { await trpc.compose.one.query({ composeId: id }); return true; }
  catch { return false; }
}

Try / catch

try {
  await trpc.compose.saveEnv.mutate({ composeId, env });
} catch (e: any) {
  if (e.code === 'BAD_REQUEST' && /environment variables/.test(e.message)) {
    await refreshComposeList(); // likely deleted concurrently
  }
}

Prevention

When it happens

Trigger: Calling compose.saveEnv (or equivalent) with a deleted/nonexistent composeId, or racing with a concurrent delete so the UPDATE matches zero rows.

Common situations: Stale UI after compose deletion, duplicate form submissions around deletion, ID typos in scripts.

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/6ebe75ff8c796ee6. Report an issue: GitHub.