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

  1. Print parseSha256Sums(input.sumsBytes.toString('utf-8')) and confirm your assetFilename matches a key exactly (case-sensitive, same separators).
  2. Verify you fetched SHA256SUMS and the asset from the SAME release tag — mismatched versions is the most common cause.
  3. 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.
  4. 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

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


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/8293088b6ac4a185. Report an issue: GitHub.