ruvnet/ruflo · error

Invalid RVFA magic: expected "RVFA", got

Error message

Invalid RVFA magic: expected "RVFA", got "${magic}"

What it means

The first 4 bytes of an RVFA image must be ASCII 'RVFA'. Anything else means the buffer is not an appliance image at all — most commonly it is an RVFP patch (magic 'RVFP'), an HTML/text error document, or an unrelated binary given a .rvf name.

Solutions

  1. Inspect the first bytes (xxd file | head -1): 'RVFA' = appliance image, 'RVFP' = patch — route each to its own parser
  2. Send patches to RvfaPatcher.parsePatchHeader, images to RvfaReader.fromBuffer
  3. Re-download from the correct CID/artifact source

Example fix

// before
const reader = RvfaReader.fromBuffer(await readFile(patchPath)); // actually an RVFP patch

// after
const buf = await readFile(path);
const kind = buf.subarray(0, 4).toString('ascii');
if (kind === 'RVFP') throw new Error('This is a patch, not an appliance — use RvfaPatcher');
if (kind !== 'RVFA') throw new Error(`Unknown artifact magic "${kind}"`);
const reader = RvfaReader.fromBuffer(buf);
Defensive patterns

Strategy: type-guard

Validate before calling

const kind = buf.subarray(0, 4).toString('ascii');
if (kind !== 'RVFA') {
  throw new Error(`Not an RVFA appliance image (magic "${kind}")`);
}

Type guard

function isRvfaImage(buf: Buffer): boolean {
  return buf.length >= 12 && buf.subarray(0, 4).toString('ascii') === 'RVFA';
}

Prevention

When it happens

Trigger: `RvfaReader.fromBuffer(buf)` on a .rvfp patch (magic 'RVFP'); a text file or gateway error page; garbage leading bytes from a corrupted transfer.

Common situations: Fetching an appliance CID that actually contains a patch; saving an error response body with the appliance filename; picking the wrong file out of a build directory.

Related errors


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

Appendix: source

Thrown at v3/@claude-flow/cli/src/appliance/rvfa-format.ts:318

export class RvfaReader {
  private buf: Buffer;
  private header: RvfaHeader;

  private constructor(buf: Buffer, header: RvfaHeader) {
    this.buf = buf;
    this.header = header;
  }

  /** Parse an RVFA image from an in-memory Buffer. */
  static fromBuffer(buf: Buffer): RvfaReader {
    if (buf.length < PREAMBLE_SIZE) {
      throw new Error('Buffer too small to contain RVFA preamble');
    }

    // Magic
    const magic = buf.subarray(0, MAGIC_SIZE).toString('ascii');
    if (magic !== 'RVFA') {
      throw new Error(`Invalid RVFA magic: expected "RVFA", got "${magic}"`);
    }

    // Version
    const version = buf.readUInt32LE(MAGIC_SIZE);
    if (version !== RVFA_VERSION) {
      throw new Error(
        `Unsupported RVFA version: ${version} (expected ${RVFA_VERSION})`,
      );
    }

    // Header length
    const headerLen = buf.readUInt32LE(MAGIC_SIZE + VERSION_SIZE);
    if (headerLen > MAX_HEADER_JSON_SIZE) {
      throw new Error(
        `Header JSON exceeds maximum size (${headerLen} > ${MAX_HEADER_JSON_SIZE})`,
      );
    }
    if (PREAMBLE_SIZE + headerLen > buf.length) {

View on GitHub (pinned to fa13ee4ad6)