nexu-io/open-design · error · DeployError

Select a valid Cloudflare domain for the custom domain.

Error message

Select a valid Cloudflare domain for the custom domain.

What it means

Thrown by normalizeCloudflarePagesDeploySelection in apps/daemon/src/deploy.ts:576 as `DeployError('Select a valid Cloudflare domain for the custom domain.', 400)` when the normalized zone name is empty or fails `isValidCloudflareZoneName`. The zone name must be a real DNS zone (e.g. `example.com`) because it becomes the suffix of the custom hostname. This guard runs only after zoneId presence is confirmed, so it catches a malformed/missing zone name even when a zoneId was supplied.

Source

Thrown at apps/daemon/src/deploy.ts:576

function normalizeDeploymentLinkStatus(status: unknown): DeployLinkStatus {
  return status === 'ready' || status === 'protected' || status === 'failed'
    ? status
    : 'link-delayed';
}

function normalizeCloudflarePagesDeploySelection(input: unknown): CloudflarePagesDeploySelection | null {
  if (!input || typeof input !== 'object') return null;
  const source = input as JsonObject;
  const rawZoneId = typeof source.zoneId === 'string' ? source.zoneId.trim() : '';
  const rawZoneName = typeof source.zoneName === 'string' ? source.zoneName.trim() : '';
  const rawPrefix = typeof source.domainPrefix === 'string' ? source.domainPrefix.trim() : '';
  if (!rawZoneId && !rawZoneName && !rawPrefix) return null;
  const zoneName = normalizeCloudflareZoneName(rawZoneName);
  const domainPrefix = normalizeCloudflareDomainPrefix(rawPrefix);
  if (!rawZoneId) throw new DeployError('Cloudflare zone is required for a custom domain.', 400);
  if (!zoneName || !isValidCloudflareZoneName(zoneName)) {
    throw new DeployError('Select a valid Cloudflare domain for the custom domain.', 400);
  }
  if (!domainPrefix) {
    throw new DeployError('Enter a valid subdomain prefix, for example "demo".', 400);
  }
  return {
    zoneId: rawZoneId,
    zoneName,
    domainPrefix,
    hostname: `${domainPrefix}.${zoneName}`,
  };
}

async function validateCloudflarePagesDeploySelection(config: DeployConfig, selection: CloudflarePagesDeploySelection | null): Promise<CloudflarePagesDeploySelection | null> {
  if (!selection) return null;
  const resp = await fetch(`${CLOUDFLARE_API}/zones/${encodeURIComponent(selection.zoneId)}`, {
    headers: cloudflareHeaders(config),
  });
  const json = await readCloudflareJson(resp);

View on GitHub (pinned to 5be4028344)

Solutions

  1. Always source `zoneName` from the zone picker (listCloudflarePagesZones result) so it matches a real zone.
  2. Validate the zone name format client-side (registered-domain pattern) before submitting.
  3. If zoneId is authoritative, look up the zone by id and populate zoneName from the API response rather than user input.

Example fix

// before
normalizeCloudflarePagesDeploySelection({ zoneId: 'abc', zoneName: '', domainPrefix: 'demo' });

// after
normalizeCloudflarePagesDeploySelection({ zoneId: 'abc', zoneName: 'example.com', domainPrefix: 'demo' });
Defensive patterns

Strategy: validation

Validate before calling

function isValidZoneName(name: string): boolean {
  return /^[a-z0-9.-]+\.[a-z]{2,}$/i.test(name) && !name.startsWith('.') && !name.endsWith('.');
}
const zoneName = typeof selection?.zoneName === 'string' ? selection.zoneName.trim() : '';
if (!zoneName || !isValidZoneName(zoneName)) {
  return res.status(400).json({ error: 'Select a valid Cloudflare domain.' });
}

Type guard

function isPlausibleZoneName(v: unknown): v is string {
  return typeof v === 'string' && /^[a-z0-9.-]+\.[a-z]{2,}$/i.test(v.trim());
}

Try / catch

try {
  await deployToCloudflarePages({ config, files, projectId, cloudflarePages: selection });
} catch (err) {
  if (err instanceof DeployError && /valid Cloudflare domain/i.test(err.message)) {
    return res.status(400).json({ error: 'Choose a valid domain from the zone list.' });
  }
  throw err;
}

Prevention

When it happens

Trigger: A selection with a present zoneId but where `rawZoneName` is empty, whitespace-only, or fails the zone-name validity check after `normalizeCloudflareZoneName` (e.g. contains invalid TLD characters, leading/trailing dots, or is an IP).

Common situations: User hand-typed a zone name instead of selecting from the picker; the zone name field desynced from the zoneId after a re-render; normalization trimmed it to empty; zone name contained illegal characters.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/8b4a958666053d3b. Report an issue: GitHub.