nexu-io/open-design · error · DeployError

Cloudflare Pages assets must be ${formatMib(CLOUDFLARE_PAGES

Error message

Cloudflare Pages assets must be ${formatMib(CLOUDFLARE_PAGES_ASSET_MAX_BYTES)} or smaller: ${file.file} is ${formatMib(data.length)}.

What it means

Cloudflare Pages rejects any single uploaded asset larger than 25 MiB (CLOUDFLARE_PAGES_ASSET_MAX_BYTES). The daemon enforces this client-side inside uploadCloudflarePagesAssets before talking to the upload API, naming the offending file and its measured size in the message.

Source

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

async function getCloudflarePagesUploadToken(config: DeployConfig): Promise<string> {
  const tokenResp = await fetch(cloudflarePagesProjectUrl(config, 'upload-token'), {
    headers: cloudflareHeaders(config),
  });
  const tokenBody = await readCloudflareJson(tokenResp);
  const jwt = tokenBody?.result?.jwt || tokenBody?.jwt;
  if (!tokenResp.ok || tokenBody?.success === false || !jwt) {
    throw cloudflareError(tokenBody, tokenResp.status, 'Cloudflare Pages upload token request failed.');
  }
  return jwt;
}

async function uploadCloudflarePagesAssets(uploadToken: string, files: DeployFile[]) {
  const uniqueFiles = new Map<string, { hash: string; data: Buffer; contentType: string }>();
  for (const file of files) {
    const data = Buffer.from(file.data);
    if (data.length > CLOUDFLARE_PAGES_ASSET_MAX_BYTES) {
      throw new DeployError(
        `Cloudflare Pages assets must be ${formatMib(CLOUDFLARE_PAGES_ASSET_MAX_BYTES)} or smaller: ${file.file} is ${formatMib(data.length)}.`,
        400,
      );
    }
    const hash = cloudflarePagesAssetHash({ ...file, data });
    if (!uniqueFiles.has(hash)) {
      uniqueFiles.set(hash, {
        hash,
        data,
        contentType: file.contentType || 'application/octet-stream',
      });
    }
  }
  const hashes = Array.from(uniqueFiles.keys());
  const missing = await cloudflarePagesMissingAssetHashes(uploadToken, hashes);
  if (missing.length > 0) {
    const missingFiles = missing.map((hash) => {
      const file = uniqueFiles.get(hash);

View on GitHub (pinned to 5be4028344)

Solutions

  1. Compress, resize, or re-encode the named asset under 25 MiB.
  2. Move large media to a CDN and reference it by URL so it is excluded from the deploy file set.
  3. Exclude the offending asset from includeProjectFiles or the deploy plan.

Example fix

// before: 40 MiB video shipped inline
files.push({ file: 'bg.mp4', data: bigVideoBuffer });

// after: host externally, reference by URL
files = files.filter((f) => f.file !== 'bg.mp4');
html = html.replace('bg.mp4', 'https://cdn.example.com/bg.mp4');
Defensive patterns

Strategy: validation

Validate before calling

import { CLOUDFLARE_PAGES_ASSET_MAX_BYTES } from './deploy.js';

function findOversizedAssets(files: DeployFile[]): DeployFile[] {
  return files.filter((f) => Buffer.byteLength(f.data) > CLOUDFLARE_PAGES_ASSET_MAX_BYTES);
}

const oversize = findOversizedAssets(files);
if (oversize.length) throw new Error(`Oversized before upload: ${oversize.map((f) => f.file).join(', ')}`);

Type guard

function isWithinAssetLimit(file: DeployFile): boolean {
  return Buffer.byteLength(file.data) <= 25 * 1024 * 1024;
}

Try / catch

try {
  await uploadCloudflarePagesAssets(token, files);
} catch (err) {
  if (err instanceof DeployError && err.status === 400 && /Cloudflare Pages assets must be/.test(err.message)) {
    // identify and compress/exclude the named asset, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: Deploying a project whose file set contains a binary over 25 MiB: an uncompressed background video, a large PDF, a font collection, or a vendored archive. The per-file size check at deploy.ts:1053 fires during the direct-upload asset staging loop.

Common situations: Generated design deck embeds a heavy media asset; unoptimized images slipped into the build; a minified bundle plus sourcemap crosses the line; vendored third-party asset that should live on a CDN.

Related errors


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