ruvnet/ruflo · error

release asset exceeds

Error message

release asset exceeds ${maxBytes} byte limit

What it means

downloadPublicAsset() refuses any asset whose declared Content-Length header exceeds the caller's cap (32 MiB for the archive, MAX_ARCHIVE_BYTES). This is the pre-download guard evaluated before the body is read, bounding memory use on untrusted release responses.

Solutions

  1. Update the ruflo CLI — the size cap is raised upstream when legitimate releases grow
  2. Inspect the reported URL manually (curl -I) and compare the real Content-Length with the official release
  3. If the official release is unexpectedly oversized, treat it as a potential supply-chain incident and report it rather than working around it
Defensive patterns

Strategy: try-catch

Validate before calling

const res = await fetch(assetUrl, { method: 'HEAD' });
const size = Number(res.headers.get('content-length') ?? 0);
if (size > 32 * 1024 * 1024) {
  console.error('archive exceeds this CLI version\'s cap — update ruflo before installing');
}

Type guard

const isSizeLimit = (e: unknown): e is Error =>
  e instanceof Error && /exceeds \d+ byte limit/.test(e.message);

Try / catch

try {
  await installProxy({ version });
} catch (e) {
  if (isSizeLimit(e)) {
    console.error('Update ruflo — the release exceeds this version\'s size cap');
    process.exit(3);
  }
  throw e;
}

Prevention

When it happens

Trigger: A genuine meta-proxy release whose archive grew past 32 MiB (added features, static linking), or a misrouted/compromised URL serving an unexpectedly large body with a truthful Content-Length.

Common situations: Upstream release size creep after the installed ruflo version was cut; mirrors that pad or wrap assets; stale cached redirects landing on a bigger file.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/548dfa3d660efb5e. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/proxy/release.ts:67

export interface ReleaseAssets {
  archiveBytes: Buffer;
  archiveFilename: string;
  sumsBytes: Buffer;
  sigBase64: string;
}

const DEV_INSTALL_ENV = 'RUFLO_DEV_PROXY_INSTALL';
const RELEASE_SOURCE_ENV = 'RUFLO_PROXY_RELEASE_SOURCE';
const GH_REPO = 'cognitum-one/meta-proxy';
const PUBLIC_DIST_BASE = 'https://github.com/cognitum-one/meta-proxy-dist/releases/download';
const MAX_ARCHIVE_BYTES = 32 * 1024 * 1024;

async function downloadPublicAsset(url: string, maxBytes: number): Promise<Buffer> {
  const response = await fetch(url, { redirect: 'follow', signal: AbortSignal.timeout(120_000) });
  if (!response.ok) throw new Error(`release download failed: HTTP ${response.status} for ${url}`);
  const declared = Number(response.headers.get('content-length') ?? 0);
  if (declared > maxBytes) throw new Error(`release asset exceeds ${maxBytes} byte limit`);
  const bytes = Buffer.from(await response.arrayBuffer());
  if (bytes.length > maxBytes) throw new Error(`release asset exceeds ${maxBytes} byte limit`);
  return bytes;
}

async function ghExecutor() {
  // Dynamic import, not a static one: @claude-flow/security is only an
  // optionalDependency of this package (see auth/security-bridge.ts for the
  // same reasoning) — a static top-level import would crash module load for
  // any consumer that doesn't have it installed, even ones that never touch
  // this dev-only download path.
  const { SafeExecutor } = await import('@claude-flow/security');
  return new SafeExecutor({ allowedCommands: ['gh'], timeout: 120_000 });
}

/**
 * Dev-only fallback: `gh release download` via SafeExecutor into `destDir`.
 * Requires the caller's environment to already have `gh` authenticated

View on GitHub (pinned to fa13ee4ad6)