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
- Always source `zoneName` from the zone picker (listCloudflarePagesZones result) so it matches a real zone.
- Validate the zone name format client-side (registered-domain pattern) before submitting.
- 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
- Source zoneName from the zone picker result, not free text.
- Match zoneName to zoneId at selection time so they cannot drift.
- Validate the domain format client-side before submit.
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
- Cloudflare zone is required for a custom domain.
- Enter a valid subdomain prefix, for example "demo".
- cloudflare_zone_mismatch
- cloudflare_zone_inactive
- cloudflare_zone_not_full
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/8b4a958666053d3b.
Report an issue: GitHub.