ruvnet/ruflo · error · Error
Footer hash must be ${SHA256_SIZE} bytes, got ${footerHash.l
Error message
Footer hash must be ${SHA256_SIZE} bytes, got ${footerHash.length} What it means
Thrown by signSections when footerHash is not exactly 32 bytes (SHA256_SIZE). The signer signs a fixed-size digest, so a wrong-size input is a caller programmer error, not a data problem.
Source
Thrown at v3/@claude-flow/cli/src/appliance/rvfa-signing.ts:327
};
// Embed signature in header and rebuild
header.signature = metadata;
const rebuilt = rebuildRvfa(buf, header, sectionData, footer);
await writeFile(rvfaPath, rebuilt);
return metadata;
}
/**
* Sign a section footer hash (detached signature).
*
* @param footerHash The 32-byte SHA256 footer hash from an RVFA file.
* @returns Hex-encoded Ed25519 signature.
*/
async signSections(footerHash: Buffer): Promise<string> {
if (footerHash.length !== SHA256_SIZE) {
throw new Error(
`Footer hash must be ${SHA256_SIZE} bytes, got ${footerHash.length}`,
);
}
const sig = sign(null, footerHash, this.keyObj);
return sig.toString('hex');
}
/**
* Sign an RVFP patch file (detached signature).
*
* @param patchData The raw patch binary data.
* @returns Hex-encoded Ed25519 signature.
*/
async signPatch(patchData: Buffer): Promise<string> {
const digest = createHash('sha256').update(patchData).digest();
const sig = sign(null, digest, this.keyObj);
return sig.toString('hex');
}View on GitHub (pinned to 6b01dc5a68)
Solutions
- Pass the 32-byte footer hash from parseRvfaBinary, or compute crypto.createHash('sha256').update(data).digest().
- If you hold a hex string, decode it first: Buffer.from(hex, 'hex').
- Confirm the digest algorithm is SHA256 (32 bytes), not SHA512 or SHA1.
Example fix
// before const sig = await signer.signSections(Buffer.from(footerHex)); // after const sig = await signer.signSections(Buffer.from(footerHex, 'hex'));
Defensive patterns
Strategy: validation
Validate before calling
if (!Buffer.isBuffer(footerHash) || footerHash.length !== 32) {
throw new Error('footerHash must be a 32-byte SHA256 Buffer');
}
const sig = await signer.signSections(footerHash); Type guard
function isSha256Digest(b: unknown): b is Buffer {
return Buffer.isBuffer(b) && b.length === 32;
} Prevention
- Keep digest bytes as Buffers end-to-end; avoid hex round-trips at API boundaries.
- Name parameters *_digest to make the 32-byte expectation explicit.
- Unit-test the signer with a known 32-byte fixture.
When it happens
Trigger: Calling signer.signSections(buf) where buf is a hex string mistaken for bytes, a 64-byte SHA512 digest, the raw footer bytes, or section data instead of the digest.
Common situations: Passing a hex-encoded string (64 ASCII bytes) instead of decoding it; passing a SHA512 digest; passing the section payload rather than its hash.
Related errors
- Buffer too small to be a valid RVFA file
- Invalid RVFA magic: expected "RVFA", got "${magic}"
- both Ed25519 private and public PEM keys are required
- unknown game "${key}". Known: ${Object.keys(GAMES).join(', '
- unknown strategy "${name}". Available: ${roster.map((r) => r
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/77d9d8d4c66ecfaf.
Report an issue: GitHub.