{"record":{"id":"d5d96f21e501ad85","repo":"nexu-io/open-design","slug":"enter-a-valid-subdomain-prefix-for-example-demo","errorCode":null,"errorMessage":"Enter a valid subdomain prefix, for example \"demo\".","messagePattern":"Enter a valid subdomain prefix, for example \"demo\"\\.","errorType":"http","errorClass":"DeployError","httpStatus":400,"severity":"error","filePath":"apps/daemon/src/deploy.ts","lineNumber":579,"sourceCode":"    ? status\n    : 'link-delayed';\n}\n\nfunction normalizeCloudflarePagesDeploySelection(input: unknown): CloudflarePagesDeploySelection | null {\n  if (!input || typeof input !== 'object') return null;\n  const source = input as JsonObject;\n  const rawZoneId = typeof source.zoneId === 'string' ? source.zoneId.trim() : '';\n  const rawZoneName = typeof source.zoneName === 'string' ? source.zoneName.trim() : '';\n  const rawPrefix = typeof source.domainPrefix === 'string' ? source.domainPrefix.trim() : '';\n  if (!rawZoneId && !rawZoneName && !rawPrefix) return null;\n  const zoneName = normalizeCloudflareZoneName(rawZoneName);\n  const domainPrefix = normalizeCloudflareDomainPrefix(rawPrefix);\n  if (!rawZoneId) throw new DeployError('Cloudflare zone is required for a custom domain.', 400);\n  if (!zoneName || !isValidCloudflareZoneName(zoneName)) {\n    throw new DeployError('Select a valid Cloudflare domain for the custom domain.', 400);\n  }\n  if (!domainPrefix) {\n    throw new DeployError('Enter a valid subdomain prefix, for example \"demo\".', 400);\n  }\n  return {\n    zoneId: rawZoneId,\n    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  }","sourceCodeStart":561,"sourceCodeEnd":597,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/deploy.ts#L561-L597","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Provide a lowercase, alphanumeric, hyphen-separated prefix such as `demo`, `app`, or `staging`.","Validate client-side: `/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/` before submit.","Make the prefix field required in the UI when a zone is selected."],"exampleFix":"// before\nnormalizeCloudflarePagesDeploySelection({ zoneId: 'abc', zoneName: 'example.com', domainPrefix: 'Demo_App!' });\n\n// after\nnormalizeCloudflarePagesDeploySelection({ zoneId: 'abc', zoneName: 'example.com', domainPrefix: 'demo' });","handlingStrategy":"validation","validationCode":"const PREFIX_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;\nconst prefix = typeof selection?.domainPrefix === 'string' ? selection.domainPrefix.trim() : '';\nif (!PREFIX_RE.test(prefix)) {\n  return res.status(400).json({ error: 'Enter a valid subdomain prefix, e.g. \"demo\".' });\n}","typeGuard":"function isValidDomainPrefix(v: unknown): v is string {\n  return typeof v === 'string' && /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/.test(v);\n}","tryCatchPattern":"try {\n  await deployToCloudflarePages({ config, files, projectId, cloudflarePages: selection });\n} catch (err) {\n  if (err instanceof DeployError && /subdomain prefix/i.test(err.message)) {\n    return res.status(400).json({ error: 'Subdomain prefix must be lowercase alphanumeric/hyphen.' });\n  }\n  throw err;\n}","preventionTips":["Lowercase and validate the prefix in the input field as the user types.","Reject underscores, spaces, uppercase, and leading/trailing hyphens client-side.","Provide an inline example (e.g. 'demo') next to the field."],"tags":["deploy","cloudflare","validation","custom-domain","dns"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}