ruvnet/ruflo · error · Error

Manifest not found

Error message

Manifest not found: ${localPath}

What it means

loadLocalWitness() throws when the --manifest path passed to `ruflo verify` does not exist on disk. The local path is the offline/air-gapped alternative to fetching the manifest from GitHub, and this guard reports the missing file before any parsing or signature verification begins.

Solutions

  1. Check the path spelling and use an absolute path to rule out cwd issues
  2. ls the file from the exact directory the CLI runs in
  3. Download or copy the manifest first (e.g. curl -fsSL -o verification.md.json <url>) then re-run verify --manifest

Example fix

# before
ruflo verify --manifest verif.json

# after
curl -fsSL -o /abs/path/verification.md.json https://raw.githubusercontent.com/ruvnet/ruflo/main/verification.md.json
ruflo verify --manifest /abs/path/verification.md.json
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync, resolve } from 'node:fs';
const manifest = resolve(manifestPath);
if (!existsSync(manifest)) {
  throw new Error(`manifest missing at ${manifest} — download it before running verify`);
}
await runVerify({ manifest });

Prevention

When it happens

Trigger: Running `ruflo verify --manifest ./verification.md.json` when the file was never downloaded, the path has a typo, or a relative path was resolved against a different working directory.

Common situations: Air-gapped verification where the download step was skipped; scripts running from a different cwd than expected; the manifest saved under a slightly different filename.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at v3/@claude-flow/cli/src/commands/verify.ts:70

  };
}

const DEFAULT_MANIFEST_URL = 'https://raw.githubusercontent.com/ruvnet/ruflo/{branch}/verification.md.json';

async function fetchWitness(branch: string): Promise<Witness> {
  const url = DEFAULT_MANIFEST_URL.replace('{branch}', branch);
  // audit_1776853149979: bare fetch had no timeout — a hung GitHub CDN would
  // pin the verify command indefinitely. 30s is generous for a sub-MB JSON.
  const res = await fetch(url, { signal: AbortSignal.timeout(30000) });
  if (!res.ok) {
    throw new Error(`Failed to fetch manifest from ${url}: ${res.status} ${res.statusText}`);
  }
  return await res.json() as Witness;
}

function loadLocalWitness(localPath: string): Witness {
  if (!existsSync(localPath)) {
    throw new Error(`Manifest not found: ${localPath}`);
  }
  return JSON.parse(readFileSync(localPath, 'utf-8')) as Witness;
}

/**
 * Locate the user's installed package root.
 *
 * The witness manifest paths are repo-relative (e.g.
 * "v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js"). For
 * end users, only the dist/ subtree ships in node_modules. We map
 * the repo path → the installed equivalent by stripping the
 * "v3/@claude-flow/<pkg>/" prefix and looking up node_modules/<pkg>/.
 */
function repoPathToInstalledPath(repoPath: string): string | null {
  // Match v3/@claude-flow/<pkg>/<rest>
  const match = repoPath.match(/^v3\/(@claude-flow\/[^/]+)\/(.+)$/);
  if (match) {
    const pkg = match[1];

View on GitHub (pinned to fa13ee4ad6)