ruvnet/ruflo · error · Error

Failed to fetch manifest from ${url}: ${res.status} ${res.st

Error message

Failed to fetch manifest from ${url}: ${res.status} ${res.statusText}

What it means

Thrown by fetchWitness() when the HTTP response to the witness-manifest fetch is not ok (res.ok === false). The fetch itself has a 30s AbortSignal timeout (added in audit_1776853149979 to avoid indefinite hangs), so this error specifically means the request completed but GitHub returned a non-2xx status.

Source

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

  integrity: {
    manifestHashAlgo: string;
    manifestHash: string;
    signatureAlgo: string;
    publicKey: string;
    signature: string;
    seedDerivation: string;
  };
}

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

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Confirm the branch exists and contains verification.md.json at the expected path.
  2. Retry shortly after a push — raw.githubusercontent.com has a short cache propagation delay.
  3. If GitHub is unreachable, supply a local manifest with the --local flag (loadLocalWitness path).
  4. Check rate-limit headers if you are scripting many verify calls.

Example fix

// before
ruflo verify --branch feature/renamed
// after
ruflo verify --branch main
# or use a local manifest:
ruflo verify --local ./verification.md.json
Defensive patterns

Strategy: retry

Validate before calling

async function fetchWitnessWithRetry(branch: string, attempts = 3): Promise<Witness> {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch(urlFor(branch), { signal: AbortSignal.timeout(30000) });
    if (res.ok) return await res.json();
    if (res.status === 404) throw new Error(`branch ${branch} has no manifest`);
    await new Promise(r => setTimeout(r, 500 * (i + 1)));
  }
  throw new Error(`manifest fetch failed after ${attempts} attempts`);
}

Type guard

const isOkResponse = (res: Response): boolean => res.ok;

Try / catch

try {
  await fetchWitness(branch);
} catch (e) {
  const msg = e instanceof Error ? e.message : String(e);
  if (msg.startsWith('Failed to fetch manifest')) {
    // fall back to a local manifest instead of retrying network blindly
    return loadLocalWitness('./verification.md.json');
  }
  throw e;
}

Prevention

When it happens

Trigger: The target branch does not exist (404), the repo/manifest path moved (404), GitHub returned 5xx during an incident, rate-limiting (403/429), or network middleware rewriting the response.

Common situations: Verifying against a branch name that was renamed/deleted, running verify right after a push before the raw.githubusercontent.com cache updates, corporate proxy returning a block page (200 with HTML, or 4xx), or GitHub outage.

Related errors


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