nexu-io/open-design · error · DeployError

cloudflare_zone_inactive

cloudflare_zone_inactive

Error message

Cloudflare custom domains require an active zone.

What it means

Thrown by validateCloudflarePagesDeploySelection in apps/daemon/src/deploy.ts:606 as `DeployError('Cloudflare custom domains require an active zone.', 400, { errorCode: 'cloudflare_zone_inactive' })` when the fetched zone has a `status` field that is not `'active'`. Cloudflare must finish provisioning the zone (nameservers verified) before it can host DNS records and a Pages custom domain, so pending/parked/moved zones are rejected. The guard is skipped only when `zone.status` is absent.

Solutions

  1. Complete Cloudflare nameserver setup for the zone (update NS records at the registrar) and wait for status to become 'active'.
  2. Verify status in the Cloudflare dashboard -> Overview for that zone.
  3. Retry the deploy/custom-domain setup after the zone shows active.

Example fix

null
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

function isZoneInactive(err: unknown): boolean {
  return err instanceof DeployError && err.status === 400
    && (err as any).details?.errorCode === 'cloudflare_zone_inactive';
}

Try / catch

try {
  await deployToCloudflarePages({ config, files, projectId, cloudflarePages: selection });
} catch (err) {
  if (isZoneInactive(err)) {
    return res.status(400).json({
      error: 'The selected zone is not active yet. Complete nameserver setup in Cloudflare and retry.',
      errorCode: 'cloudflare_zone_inactive',
    });
  }
  throw err;
}

Prevention

When it happens

Trigger: The selected zone resolves and matches by name, but its Cloudflare-reported `status` is e.g. `pending` (nameservers not yet verified), `moved`, `deactivated`, or `initializing`.

Common situations: Newly added zone where nameserver verification hasn't completed; zone temporarily deactivated; zone in the process of being moved away from Cloudflare.

Related errors


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

Appendix: source

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

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.');
  }
  const zone = json?.result ?? json;
  const zoneName = normalizeCloudflareZoneName(zone?.name);
  if (!zoneName || zoneName !== selection.zoneName) {
    throw new DeployError('Cloudflare zone selection no longer matches the selected domain.', 400, {
      errorCode: 'cloudflare_zone_mismatch',
    });
  }
  if (zone?.status && zone.status !== 'active') {
    throw new DeployError('Cloudflare custom domains require an active zone.', 400, {
      errorCode: 'cloudflare_zone_inactive',
    });
  }
  if (zone?.type && zone.type !== 'full') {
    throw new DeployError('Cloudflare custom domains require a full DNS zone.', 400, {
      errorCode: 'cloudflare_zone_not_full',
    });
  }
  return { ...selection, zoneName };
}

async function setupCloudflarePagesCustomDomain({ config, projectId, selection, pagesDevUrl, priorMetadata }: { config: DeployConfig; projectId: string; selection: CloudflarePagesDeploySelection; pagesDevUrl: string; priorMetadata?: JsonObject | undefined }) {
  if (!config.projectName) throw new DeployError('Cloudflare Pages project name could not be generated.', 400);
  const pagesTarget = normalizeHostname(hostnameFromUrl(pagesDevUrl) || `${config.projectName}.pages.dev`);
  const marker = cloudflarePagesDnsMarker(projectId, config.projectName, pagesTarget);
  const base = {
    hostname: selection.hostname,
    url: `https://${selection.hostname}`,

View on GitHub (pinned to 5be4028344)