ruvnet/ruflo · error · ReleaseVerificationError
SHA256SUMS has no entry for ${input.assetFilename}
Error message
SHA256SUMS has no entry for ${input.assetFilename} What it means
Thrown by verifyRelease() after the Ed25519 signature on SHA256SUMS has already passed, but the requested assetFilename has no matching line in the parsed SHA256SUMS file. This is the second of three all-or-nothing gates in ADR-307's release verification: signature first, then entry presence, then hash match. The signer is trusted (sig passed), so this specifically means the filename you asked to verify is not covered by that signed manifest.
Source
Thrown at v3/@claude-flow/cli/src/proxy/verify.ts:85
export interface VerifyReleaseResult {
sha256: string;
}
/**
* Full verification: signature over SHA256SUMS, then the asset's own hash
* against the matching line. Throws `ReleaseVerificationError` on ANY
* failure — there is no partial-trust outcome, matching ADR-307's "refuses
* on any mismatch" requirement.
*/
export function verifyRelease(input: VerifyReleaseInput): VerifyReleaseResult {
if (!verifySha256SumsSignature(input.sumsBytes, input.sigBase64, input.pubkeyPem)) {
throw new ReleaseVerificationError('SHA256SUMS.sig failed Ed25519 verification — refusing to install');
}
const sums = parseSha256Sums(input.sumsBytes.toString('utf-8'));
const expected = sums[input.assetFilename];
if (!expected) {
throw new ReleaseVerificationError(`SHA256SUMS has no entry for ${input.assetFilename}`);
}
const actual = sha256Hex(input.assetBytes);
if (actual !== expected) {
throw new ReleaseVerificationError(
`sha256 mismatch for ${input.assetFilename}: expected ${expected.slice(0, 12)}…, got ${actual.slice(0, 12)}…`,
);
}
return { sha256: actual };
}
View on GitHub (pinned to 6b01dc5a68)
Solutions
- Print parseSha256Sums(input.sumsBytes.toString('utf-8')) and confirm your assetFilename matches a key exactly (case-sensitive, same separators).
- Verify you fetched SHA256SUMS and the asset from the SAME release tag — mismatched versions is the most common cause.
- If the asset uses a path prefix in the manifest (e.g. 'dist/rufvo-linux-x64'), pass the full key as it appears in SHA256SUMS, not just the basename.
- If the manifest genuinely lacks the entry, treat it as a release-packaging defect: refuse to install and report upstream — do not loosen the check.
Example fix
// before — basename only, manifest lists prefixed path
verifyRelease({ assetFilename: 'ruflo-linux-x64', sumsBytes, sigBase64, assetBytes });
// after — match the exact key present in SHA256SUMS
const sums = parseSha256Sums(sumsBytes.toString('utf-8'));
const key = Object.keys(sums).find(k => k.endsWith('ruflo-linux-x64'));
if (!key) throw new Error('asset not in manifest — wrong release?');
verifyRelease({ assetFilename: key, sumsBytes, sigBase64, assetBytes }); Defensive patterns
Strategy: validation
Validate before calling
import { parseSha256Sums } from './verify';
function findAssetKey(sumsBytes: Buffer, requestedAsset: string): string | null {
const sums = parseSha256Sums(sumsBytes.toString('utf-8'));
if (sums[requestedAsset]) return requestedAsset; // exact match
// tolerate path/separator differences
const norm = (s: string) => s.replace(/\\/g, '/').toLowerCase();
const want = norm(requestedAsset);
for (const key of Object.keys(sums)) {
if (norm(key) === want || norm(key).endsWith('/' + want)) return key;
}
return null;
}
// before calling verifyRelease:
const key = findAssetKey(sumsBytes, assetFilename);
if (!key) throw new Error(`asset '${assetFilename}' not in SHA256SUMS; available: ${Object.keys(parseSha256Sums(sumsBytes.toString())).slice(0,5).join(', ')}...`); Try / catch
try {
verifyRelease({ assetFilename: key, sumsBytes, sigBase64, assetBytes });
} catch (e) {
if (e instanceof ReleaseVerificationError && e.message.startsWith('SHA256SUMS has no entry')) {
// filename mismatch — do NOT retry with a different filename blindly;
// surface the manifest keys so the operator can pick the right one.
throw new Error(`${e.message}. Manifest entries: ${Object.keys(parseSha256Sums(sumsBytes.toString('utf-8'))).join(', ')}`);
}
throw e;
} Prevention
- Always fetch SHA256SUMS and the asset from the same release tag in the same HTTP session.
- Log Object.keys(parseSha256Sums(sumsBytes)) once at startup so missing-asset errors are debuggable.
- Treat the asset filename as opaque — pass exactly the key that appears in the manifest.
- Never auto-retry by stripping path prefixes; a missing entry is a release-packaging signal, not a normalization issue.
When it happens
Trigger: Calling verifyRelease({ assetFilename: 'ruflo-darwin-arm64', ... }) when SHA256SUMS only lists 'ruflo-linux-x64'; passing a filename with a different path prefix/suffix (e.g. 'bin/ruflo' vs 'ruflo'); using a SHA256SUMS from release v0.1.0 to verify an asset from v0.2.0; passing a Windows-style backslash filename while the manifest uses forward slashes; passing the directory name instead of the asset basename.
Common situations: CI pipeline fetches the SHA256SUMS for the wrong release tag; the proxy downloads assets and sums from two different GitHub release pages; a release publisher forgot to include the new platform's binary in SHA256SUMS; filename was normalized (case-folded, trailing whitespace) before lookup but the manifest has the raw form.
Related errors
- sha256 mismatch for ${input.assetFilename}: expected ${expec
- SSRF guard: invalid URL — ${rawUrl}
- SSRF guard: only HTTPS URLs are permitted, got ${parsed.prot
- SSRF guard: private/loopback host rejected — ${host}
- SSRF guard: invalid URL — ${rawUrl}
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/8293088b6ac4a185.
Report an issue: GitHub.