Dokploy/dokploy · warning · TRPCError

BAD_REQUEST

BAD_REQUEST

Error message

Credentials must be re-entered when changing the provider type

What it means

mergeDnsProviderConfig merges an incoming provider config with stored secrets: when a sensitive field arrives as the mask placeholder (DNS_SECRET_MASK) and the provider type is changing, it cannot backfill the old provider's secret, so it throws BAD_REQUEST. This prevents silently storing the mask or copying secrets between incompatible providers.

Source

Thrown at packages/server/src/services/dns-provider.ts:42

): DnsProviderConfig => {
	const masked: Record<string, unknown> = { ...config };
	for (const field of SENSITIVE_FIELDS[config.providerType]) {
		if (masked[field]) {
			masked[field] = DNS_SECRET_MASK;
		}
	}
	return masked as DnsProviderConfig;
};

export const mergeDnsProviderConfig = (
	incoming: DnsProviderConfig,
	existing: DnsProviderConfig,
): DnsProviderConfig => {
	const merged: Record<string, unknown> = { ...incoming };
	for (const field of SENSITIVE_FIELDS[incoming.providerType]) {
		if (merged[field] === DNS_SECRET_MASK) {
			if (incoming.providerType !== existing.providerType) {
				throw new TRPCError({
					code: "BAD_REQUEST",
					message:
						"Credentials must be re-entered when changing the provider type",
				});
			}
			merged[field] = (existing as Record<string, unknown>)[field];
		}
	}
	return merged as DnsProviderConfig;
};

const isUniqueNameViolation = (error: unknown) =>
	error instanceof Error && error.message.includes("dns_provider_org_name_idx");

export const createDnsProvider = async (
	input: z.infer<typeof apiCreateDnsProvider>,
	organizationId: string,
) => {

View on GitHub (pinned to 546686ea35)

Solutions

  1. Re-enter the credentials (API key/token/secret) for the new provider type when switching it
  2. Or keep the same providerType if you only meant to update non-secret fields
  3. If building a client: omit sensitive fields entirely instead of sending the mask value when changing providerType

Example fix

// before (client sends mask while changing type)
{ providerType: "route53", apiKey: "********" }
// after (send real secret, or omit field)
{ providerType: "route53", apiKey: "AKIA..." }
Defensive patterns

Strategy: validation

Validate before calling

const changingType = input.providerType !== existing.providerType;
const masked = SENSITIVE_FIELDS[input.providerType].some((f) => input[f] === DNS_SECRET_MASK);
if (changingType && masked) throw new Error("Re-enter credentials when changing provider type");

Type guard

const isMaskedConfig = (cfg: Record<string, unknown>) =>
  Object.values(cfg).some((v) => v === DNS_SECRET_MASK);

Try / catch

try { await updateDnsProviderConfig(input); } catch (e) { if (e instanceof TRPCError && e.code === "BAD_REQUEST") { /* prompt user for fresh credentials */ } }

Prevention

When it happens

Trigger: Updating a DNS provider while changing providerType (e.g. Cloudflare → Route53) and leaving masked credential fields as-is (the UI sends the mask for unchanged secrets). Secrets for the new provider type must be re-entered.

Common situations: Editing a DNS provider in the UI and switching the provider dropdown without re-typing API credentials; API clients PATCHing providerType plus masked fields copied from a GET response.

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/97705fb6f37f8961. Report an issue: GitHub.