nexu-io/open-design · error · DeployError

Enter a valid subdomain prefix, for example "demo".

Error message

Enter a valid subdomain prefix, for example "demo".

What it means

Thrown by normalizeCloudflarePagesDeploySelection in apps/daemon/src/deploy.ts:579 as `DeployError('Enter a valid subdomain prefix, for example "demo".', 400)` when the normalized domain prefix is empty. The prefix becomes the leftmost label of the custom hostname (`${prefix}.${zone}`), so an empty/invalid prefix yields no usable subdomain. `normalizeCloudflareDomainPrefix` strips anything that isn't a valid DNS label.

Source

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

    ? 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);
  if (!resp.ok || json?.success === false) {
    throw cloudflareError(json, resp.status, 'Cloudflare zone lookup failed.');
  }

View on GitHub (pinned to 5be4028344)

Solutions

  1. Provide a lowercase, alphanumeric, hyphen-separated prefix such as `demo`, `app`, or `staging`.
  2. Validate client-side: `/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/` before submit.
  3. Make the prefix field required in the UI when a zone is selected.

Example fix

// before
normalizeCloudflarePagesDeploySelection({ zoneId: 'abc', zoneName: 'example.com', domainPrefix: 'Demo_App!' });

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

Strategy: validation

Validate before calling

const PREFIX_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
const prefix = typeof selection?.domainPrefix === 'string' ? selection.domainPrefix.trim() : '';
if (!PREFIX_RE.test(prefix)) {
  return res.status(400).json({ error: 'Enter a valid subdomain prefix, e.g. "demo".' });
}

Type guard

function isValidDomainPrefix(v: unknown): v is string {
  return typeof v === 'string' && /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/.test(v);
}

Try / catch

try {
  await deployToCloudflarePages({ config, files, projectId, cloudflarePages: selection });
} catch (err) {
  if (err instanceof DeployError && /subdomain prefix/i.test(err.message)) {
    return res.status(400).json({ error: 'Subdomain prefix must be lowercase alphanumeric/hyphen.' });
  }
  throw err;
}

Prevention

When it happens

Trigger: A custom-domain selection where `domainPrefix`, after trimming and normalization, is empty — e.g. the user submitted only zoneId+zoneName with no prefix, or the prefix contained only illegal characters that normalization removed.

Common situations: Prefix field left blank; user entered an invalid prefix (uppercase, underscores, spaces, punctuation) that normalizeCloudflareDomainPrefix reduced to empty string; the wildcard/root intent was misunderstood (the deploy requires a subdomain label).

Related errors


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