nexu-io/open-design · error · DeployError

Cloudflare account ID is required.

Error message

Cloudflare account ID is required.

What it means

Thrown by deployToCloudflarePages in apps/daemon/src/deploy.ts:480 as `DeployError('Cloudflare account ID is required.', 400)` — the second guard, immediately after the token check. The Cloudflare Pages API scopes project/asset/custom-domain operations under an account id (`account.id`), so a token alone is insufficient. Even if the token is valid, the deploy cannot proceed without the account ID.

Source

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

        status: typeof zone?.status === 'string' ? zone.status : undefined,
        type: typeof zone?.type === 'string' ? zone.type : undefined,
      }))
      .filter((zone) => zone.id && zone.name),
    cloudflarePages: normalizeCloudflarePagesConfigHints(config?.cloudflarePages),
  };
}

export async function deployToCloudflarePages(input: { config: DeployConfig; files: DeployFile[]; projectId?: string; cloudflarePages?: unknown; priorMetadata?: JsonObject | undefined; target?: 'preview' | 'production' }) {
  const {
    config,
    files,
    projectId = '',
    cloudflarePages = undefined,
    priorMetadata = undefined,
    target = 'production',
  } = input || {};
  if (!config?.token) throw new DeployError('Cloudflare API token is required.', 400);
  if (!config?.accountId) throw new DeployError('Cloudflare account ID is required.', 400);
  if (!config?.projectName) throw new DeployError('Cloudflare Pages project name could not be generated.', 400);

  const customDomainSelection = await validateCloudflarePagesDeploySelection(
    config,
    normalizeCloudflarePagesDeploySelection(cloudflarePages),
  );

  await ensureCloudflarePagesProject(config);

  const uploadToken = await getCloudflarePagesUploadToken(config);
  await uploadCloudflarePagesAssets(uploadToken, files);

  const form = new FormData();
  const manifest: Record<string, string> = {};
  for (const file of files) {
    manifest[`/${file.file}`] = cloudflarePagesAssetHash(file);
  }
  form.append('manifest', JSON.stringify(manifest));

View on GitHub (pinned to 5be4028344)

Solutions

  1. Save the Cloudflare account ID alongside the token via writeCloudflarePagesConfig({ token, accountId }).
  2. Find the account ID in the Cloudflare dashboard sidebar (right side on most pages) or via `GET /accounts` with the token.
  3. Gate the deploy on both token and accountId being non-empty before calling deployToCloudflarePages.

Example fix

// before
await deployToCloudflarePages({ config: { token: '...', accountId: '' }, files, projectId });

// after
await writeCloudflarePagesConfig({ token: '...', accountId: 'abc123' });
const config = await readCloudflarePagesConfig();
await deployToCloudflarePages({ config, files, projectId });
Defensive patterns

Strategy: validation

Validate before calling

const config = await readCloudflarePagesConfig();
if (!config.token) return res.status(400).json({ error: 'Token required.' });
if (!config.accountId) return res.status(400).json({ error: 'Account ID required.' });
await deployToCloudflarePages({ config, files, projectId });

Type guard

function hasCloudflareAccount(config: Partial<DeployConfig>): config is DeployConfig & { accountId: string } {
  return typeof config.accountId === 'string' && config.accountId.trim().length > 0;
}

Try / catch

try {
  await deployToCloudflarePages({ config, files, projectId });
} catch (err) {
  if (err instanceof DeployError && err.status === 400 && /account ID/i.test(err.message)) {
    return res.status(400).json({ error: 'Enter your Cloudflare account ID.' });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling `deployToCloudflarePages({ config, ... })` where `config.token` is non-empty but `config.accountId` is empty. Typically the user saved only a token (or the token field) and left the account ID blank.

Common situations: Config write persisted token but not accountId; user pasted the token into the wrong field; account ID field omitted in the UI form; accountId trimmed to empty due to whitespace-only input.

Related errors


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