{"record":{"id":"6b594b7d4452b4f8","repo":"nexu-io/open-design","slug":"cloudflare-zone-mismatch","errorCode":"cloudflare_zone_mismatch","errorMessage":"Cloudflare zone selection no longer matches the selected domain.","messagePattern":"Cloudflare zone selection no longer matches the selected domain\\.","errorType":"http","errorClass":"DeployError","httpStatus":400,"severity":"error","filePath":"apps/daemon/src/deploy.ts","lineNumber":601,"sourceCode":"    zoneName,\n    domainPrefix,\n    hostname: `${domainPrefix}.${zoneName}`,\n  };\n}\n\nasync function validateCloudflarePagesDeploySelection(config: DeployConfig, selection: CloudflarePagesDeploySelection | null): Promise<CloudflarePagesDeploySelection | null> {\n  if (!selection) return null;\n  const resp = await fetch(`${CLOUDFLARE_API}/zones/${encodeURIComponent(selection.zoneId)}`, {\n    headers: cloudflareHeaders(config),\n  });\n  const json = await readCloudflareJson(resp);\n  if (!resp.ok || json?.success === false) {\n    throw cloudflareError(json, resp.status, 'Cloudflare zone lookup failed.');\n  }\n  const zone = json?.result ?? json;\n  const zoneName = normalizeCloudflareZoneName(zone?.name);\n  if (!zoneName || zoneName !== selection.zoneName) {\n    throw new DeployError('Cloudflare zone selection no longer matches the selected domain.', 400, {\n      errorCode: 'cloudflare_zone_mismatch',\n    });\n  }\n  if (zone?.status && zone.status !== 'active') {\n    throw new DeployError('Cloudflare custom domains require an active zone.', 400, {\n      errorCode: 'cloudflare_zone_inactive',\n    });\n  }\n  if (zone?.type && zone.type !== 'full') {\n    throw new DeployError('Cloudflare custom domains require a full DNS zone.', 400, {\n      errorCode: 'cloudflare_zone_not_full',\n    });\n  }\n  return { ...selection, zoneName };\n}\n\nasync function setupCloudflarePagesCustomDomain({ config, projectId, selection, pagesDevUrl, priorMetadata }: { config: DeployConfig; projectId: string; selection: CloudflarePagesDeploySelection; pagesDevUrl: string; priorMetadata?: JsonObject | undefined }) {\n  if (!config.projectName) throw new DeployError('Cloudflare Pages project name could not be generated.', 400);","sourceCodeStart":583,"sourceCodeEnd":619,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/deploy.ts#L583-L619","documentation":"Thrown by validateCloudflarePagesDeploySelection in apps/daemon/src/deploy.ts:601 as `DeployError('Cloudflare zone selection no longer matches the selected domain.', 400, { errorCode: 'cloudflare_zone_mismatch' })`. After fetching the live zone by id from Cloudflare and normalizing its name, if it differs from the selection's stored zoneName, the selection is treated as stale/inconsistent and rejected. The `errorCode` lets the UI present this distinctly from generic 400s.","triggerScenarios":"A selection carries a zoneId+zoneName pair, the zone lookup succeeds (resp.ok and success !== false), but `zone.name` from the API normalized does not equal `selection.zoneName`. Typically the zone was renamed, the zoneId now points to a different zone, or the selection was crafted from stale cached data.","commonSituations":"User picked a zone earlier, then the zone was renamed/deleted/recreated in Cloudflare; the selection was persisted across a session and the zone list changed; a different account's zoneId leaked into the selection; frontend cached an old zone name.","solutions":["Re-fetch zones via listCloudflarePagesZones and let the user re-select the zone to refresh both zoneId and zoneName.","Drive the selection from a single source (the picker) instead of stitching zoneId and zoneName from different states.","Handle errorCode 'cloudflare_zone_mismatch' in the UI by clearing the saved selection and reopening the picker."],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"function isZoneMismatch(err: unknown): boolean {\n  return err instanceof DeployError && err.status === 400\n    && (err as any).details?.errorCode === 'cloudflare_zone_mismatch';\n}","tryCatchPattern":"try {\n  await deployToCloudflarePages({ config, files, projectId, cloudflarePages: selection });\n} catch (err) {\n  if (isZoneMismatch(err)) {\n    return res.status(400).json({\n      error: 'The selected zone changed. Re-select the domain and retry.',\n      errorCode: 'cloudflare_zone_mismatch',\n    });\n  }\n  throw err;\n}","preventionTips":["Refresh zones via listCloudflarePagesZones whenever the picker is opened, so selections use current data.","Bind zoneId and zoneName to the same picker event so they never desync.","On a mismatch, clear the persisted selection and force a fresh pick rather than retrying blindly."],"tags":["deploy","cloudflare","custom-domain","zone","stale-state"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}