ruvnet/ruflo · error

Invalid RVFP magic

Error message

Invalid RVFP magic: "${magic}"

What it means

Bytes 0–3 of an RVFP patch must ASCII-decode to 'RVFP'. Anything else fails here — including the sibling appliance magic 'RVFA' — meaning the file is simply not an RVFP patch: wrong artifact type, a text/HTML document saved with a .rvfp name, or corrupted leading bytes.

Solutions

  1. Identify the artifact: appliances start with 'RVFA' (use RvfaReader), patches with 'RVFP' (use RvfaPatcher) — feed each to its own parser
  2. If downloaded, inspect the head of the file (xxd file | head -1) to see what it actually is
  3. Re-download or rebuild via RvfaPatcher.createPatch

Example fix

// before
const header = RvfaPatcher.parsePatchHeader(await readFile(appliancePath)); // actually an RVFA image

// after
const buf = await readFile(patchPath);
if (buf.subarray(0, 4).toString('ascii') === 'RVFA') {
  throw new Error('This is an appliance image, not a patch — use RvfaReader.fromBuffer');
}
const header = RvfaPatcher.parsePatchHeader(buf);
Defensive patterns

Strategy: type-guard

Validate before calling

const magic = buf.subarray(0, 4).toString('ascii');
if (magic !== 'RVFP') {
  throw new Error(`Expected RVFP patch, got magic "${magic}"`);
}

Type guard

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

Prevention

When it happens

Trigger: `parsePatchHeader` on a full .rvf appliance image (magic 'RVFA'); a gateway JSON/HTML error body saved as .rvfp; a binary whose first four bytes were overwritten by corruption or wrong-offset slicing.

Common situations: Confusing an appliance image with its patch (both start 'RVF'); downloading from the wrong CID; a proxy injecting an error page with a 200 status.

Related errors


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

Appendix: source

Thrown at v3/@claude-flow/cli/src/appliance/rvfa-distribution.ts:193

      created: new Date().toISOString(), newSectionSize: payload.length,
      newSectionSha256: sha256(payload), compression: comp,
    };
    if (opts.privateKey && opts.signedBy) {
      const signable = Buffer.concat([Buffer.from(canonicalJson(header), 'utf-8'), payload]);
      header.signature = edSign(signable, opts.privateKey);
      header.signedBy = opts.signedBy;
    }
    const hJson = Buffer.from(JSON.stringify(header), 'utf-8');
    const magic = Buffer.from('RVFP');
    const ver = Buffer.alloc(4); ver.writeUInt32LE(RVFP_VERSION, 0);
    const hLen = Buffer.alloc(4); hLen.writeUInt32LE(hJson.length, 0);
    return Buffer.concat([magic, ver, hLen, hJson, payload, sha256B(payload)]);
  }

  static parsePatchHeader(buf: Buffer): RvfpHeader {
    if (buf.length < PRE) throw new Error('Buffer too small for RVFP preamble');
    const magic = buf.subarray(0, 4).toString('ascii');
    if (magic !== 'RVFP') throw new Error(`Invalid RVFP magic: "${magic}"`);
    const ver = buf.readUInt32LE(4);
    if (ver !== RVFP_VERSION) throw new Error(`Unsupported RVFP version: ${ver}`);
    const hLen = buf.readUInt32LE(8);
    if (PRE + hLen > buf.length) throw new Error('Buffer too small for declared header');
    const h = JSON.parse(buf.subarray(PRE, PRE + hLen).toString('utf-8')) as RvfpHeader;
    if (h.magic !== 'RVFP') throw new Error('RVFP header magic mismatch');
    return h;
  }

  static async verifyPatch(buf: Buffer): Promise<PatchVerifyResult> {
    const errors: string[] = [];
    let header: RvfpHeader;
    try { header = RvfaPatcher.parsePatchHeader(buf); } catch (e) {
      const empty: RvfpHeader = {
        magic: 'RVFP', version: 0, targetApplianceName: '', targetApplianceVersion: '',
        targetSection: '', patchVersion: '', created: '', newSectionSize: 0,
        newSectionSha256: '', compression: 'none',
      };

View on GitHub (pinned to fa13ee4ad6)