anomalyco/sst · error · Error

Could not find hosted zone for domain ${inputs.domain}

Error message

Could not find hosted zone for domain ${inputs.domain}

What it means

Before binding a domain, SST queries Cloudflare's zone list API to find the hosted zone matching the domain. If all pages of zones are exhausted without a match, it throws because Cloudflare cannot route a domain it doesn't manage.

Source

Thrown at platform/src/components/cloudflare/providers/zone-lookup.ts:60

  ): Promise<{ zoneId: string; zoneName: string }> {
    try {
      const qs = new URLSearchParams({
        per_page: "50",
        page: String(page),
        "account.id": inputs.accountId,
      }).toString();
      const ret = await cfFetch<{ name: string; id: string }[]>(
        `/zones?${qs}`,
        { headers: { "Content-Type": "application/json" } },
      );
      const zone = ret.result.find(
        // ensure `example.com` does not match `myexample.com`
        (z) => inputs.domain === z.name || inputs.domain.endsWith(`.${z.name}`),
      );
      if (zone) return { zoneId: zone.id, zoneName: zone.name };

      if (ret.result.length < ret.result_info!.per_page)
        throw new Error(
          `Could not find hosted zone for domain ${inputs.domain}`,
        );

      return this.lookup(inputs, page + 1);
    } catch (error: any) {
      console.log(error);
      throw error;
    }
  }
}

export class ZoneLookup extends dynamic.Resource {
  constructor(
    name: string,
    args: ZoneLookupInputs,
    opts?: CustomResourceOptions,
  ) {
    super(

View on GitHub (pinned to a0bd20f762)

Solutions

  1. Add the domain (parent zone) to your Cloudflare account and update nameservers.
  2. Check the Cloudflare API token has Zone:Read permission and covers the right account.
  3. Verify the domain string in your sst.config.ts for typos and that the parent zone exists in the account.

Example fix

// before
const zone = cloudflare.getZoneOutput({ name: "app.example.com" }); // parent zone not in account
// after — ensure example.com is added to the Cloudflare account first, then:
dns: cloudflare.dns({ domain: "app.example.com" })
Defensive patterns

Strategy: retry

Validate before calling

// pre-check via Cloudflare API before deploy
const res = await fetch(`https://api.cloudflare.com/client/v4/zones?name=${parentDomain}`, { headers: { Authorization: `Bearer ${token}` } });
const json = await res.json();
if (!json.result?.length) throw new Error("Parent zone not in this Cloudflare account");

Try / catch

try {
  new sst.cloudflare.StaticSite(app, "Site", { domain: { name: "app.example.com", dns: cloudflare.dns() } });
} catch (e) {
  if (String(e).includes("Could not find hosted zone")) console.error("Add the zone to Cloudflare or fix the API token");
  else throw e;
}

Prevention

When it happens

Trigger: lookup() paginates through account zones and no zone name equals or is a parent of `inputs.domain` (exact match or subdomain ending in `.zonename`); thrown when the last page is reached (`result.length < per_page`) with no match.

Common situations: Domain's DNS is hosted elsewhere (not in the API-token's Cloudflare account); API token lacks Zone:Read permission so zones list is empty; typo in the domain; subdomain whose parent zone isn't in this account; zone not yet added to Cloudflare.

Related errors


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/33d9a3d1821dca5f. Report an issue: GitHub.