Dokploy/dokploy · warning · TRPCError

BAD_REQUEST

BAD_REQUEST

Error message

Provide a config or a dnsProviderId to test

What it means

Thrown by `dnsProvider.testConnection` when the mutation is called with neither an inline `config` nor an existing `dnsProviderId`. The procedure needs provider credentials to run a DNS test query, and with both inputs absent there is nothing to test, so it fails fast with BAD_REQUEST before touching any DNS API.

Source

Thrown at apps/dokploy/server/api/routers/dns-provider.ts:111

			config: maskDnsProviderConfig(provider.config),
		}));
	}),

	one: withPermission("dnsProvider", "read")
		.input(apiFindOneDnsProvider)
		.query(async ({ ctx, input }) => {
			const provider = await findDnsProviderInOrganization(
				input.dnsProviderId,
				ctx.session.activeOrganizationId,
			);
			return { ...provider, config: maskDnsProviderConfig(provider.config) };
		}),

	testConnection: withPermission("dnsProvider", "create")
		.input(apiTestDnsProvider)
		.mutation(async ({ ctx, input }) => {
			if (!input.config && !input.dnsProviderId) {
				throw new TRPCError({
					code: "BAD_REQUEST",
					message: "Provide a config or a dnsProviderId to test",
				});
			}

			let config = input.config;
			if (input.dnsProviderId) {
				const provider = await findDnsProviderInOrganization(
					input.dnsProviderId,
					ctx.session.activeOrganizationId,
				);
				config = config
					? mergeDnsProviderConfig(config, provider.config)
					: provider.config;
			}

			await testDnsProviderConnection(config!);
			return true;

View on GitHub (pinned to 546686ea35)

Solutions

  1. Pass an inline config (provider, credentials, etc.) when testing unsaved credentials
  2. Or pass the `dnsProviderId` of a saved provider to test its stored credentials
  3. Guard the UI: disable the Test button until the form yields a non-empty config or a saved provider is chosen
  4. Fix clients that send `{}` — log the payload before calling to catch this early

Example fix

// before
await dnsProvider.testConnection.mutate({});
// after
await dnsProvider.testConnection.mutate({
  config: { provider: 'cloudflare', cloudflareApiToken: token },
});
// or, for a saved provider:
await dnsProvider.testConnection.mutate({ dnsProviderId });
Defensive patterns

Strategy: validation

Validate before calling

if (!config && !dnsProviderId) {
  throw new Error('provide a config or dnsProviderId to test');
}
await dnsProvider.testConnection.mutate(config ? { config } : { dnsProviderId });

Type guard

const hasTestInput = (
  i: { config?: unknown; dnsProviderId?: string },
): i is { config: NonNullable<typeof i.config> } | { dnsProviderId: string } =>
  Boolean(i.config ?? i.dnsProviderId);

Try / catch

try {
  await dnsProvider.testConnection.mutate(payload);
} catch (e) {
  if (e instanceof TRPCClientError && e.data?.code === 'BAD_REQUEST') {
    // populate the form config or select a saved provider, then retry
  }
}

Prevention

When it happens

Trigger: Calling `dnsProvider.testConnection` with an empty payload `{}`; passing only whitespace/empty provider fields so `input.config` is falsy while omitting `dnsProviderId`; frontend sending the test request before the form has produced a config object.

Common situations: UI 'Test connection' button enabled before any provider is selected or credentials typed; migration scripts calling the endpoint with a bare object; Zod schema treating an all-optional config as valid (empty object passes validation but fails this runtime check).

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/92656abe3dd12b26. Report an issue: GitHub.