ruvnet/ruflo · error

flywheel anchor manifest requires path and sha256

Error message

flywheel anchor manifest requires path and sha256

What it means

Beyond the schema version, the manifest must carry `path` (string) and `sha256` (string) — the location of the tasks file and its pinned content hash. Missing or non-string fields throw before any filesystem access, because a manifest without both keys cannot pin an anchor and downstream selection would be unverified.

Solutions

  1. Add both fields: `"path": ".claude/eval/tasks.json"` (project- or manifest-relative) and `"sha256": "sha256:<64 hex>"`
  2. Compute the hash from the tasks file with the exported `humanEvalHash` helper (it emits the `sha256:` prefix and tolerates normalization downstream)
  3. Validate the manifest JSON in CI before the harness runs

Example fix

// before
{ "schemaVersion": "ruflo.flywheel-anchor-manifest/v1", "path": ".claude/eval/tasks.json" }

// after
{ "schemaVersion": "ruflo.flywheel-anchor-manifest/v1", "path": ".claude/eval/tasks.json", "sha256": "sha256:6096e48e…" }
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'node:fs';

function manifestFieldsOk(file: string): boolean {
  const m = JSON.parse(readFileSync(file, 'utf8'));
  return typeof m.path === 'string' && typeof m.sha256 === 'string' && m.path.length > 0 && m.sha256.length > 0;
}

Type guard

interface AnchorManifest { schemaVersion: string; path: string; sha256: string; }

function hasManifestFields(v: unknown): v is AnchorManifest {
  if (typeof v !== 'object' || v === null) return false;
  const o = v as Record<string, unknown>;
  return typeof o.path === 'string' && typeof o.sha256 === 'string';
}

Try / catch

try {
  return loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
  if (e?.message === 'flywheel anchor manifest requires path and sha256') {
    throw new Error(`Manifest at ${manifestFile} must set "path" (tasks file location) and "sha256" (its pinned content hash).`);
  }
  throw e;
}

Prevention

When it happens

Trigger: A manifest with only `path` (hash forgotten), only `sha256`, either field non-string (`path: null`, a numeric hash), or an empty JSON object at the manifest location.

Common situations: Hand-authored manifests; generators omitting fields they treat as optional; partial config merges; hashes stored as raw hex without the `sha256:` prefix in `sha256` fields of other tools pasted in.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of ruvnet/ruflo@2602b642d9 (2026-08-18). Data as JSON: /api/errors/3a5028ce8516f3f7. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/services/harness-project-anchor.ts:173

  if (!!options.anchorPath !== !!options.anchorHash) {
    throw new Error('anchorPath and anchorHash must be supplied together');
  }
  if (options.anchorPath && options.anchorHash) {
    return toSelection(containedPath(root, options.anchorPath), options.anchorHash);
  }

  const manifestCandidate = options.manifestPath ?? DEFAULT_PROJECT_ANCHOR_MANIFEST;
  const manifestPath = isAbsolute(manifestCandidate)
    ? manifestCandidate
    : resolve(root, manifestCandidate);
  if (existsSync(manifestPath)) {
    const containedManifest = containedPath(root, manifestPath);
    const manifest = JSON.parse(readFileSync(containedManifest, 'utf8')) as ProjectAnchorManifest;
    if (manifest.schemaVersion !== PROJECT_ANCHOR_MANIFEST_SCHEMA) {
      throw new Error(`unsupported flywheel anchor manifest schema: ${manifest.schemaVersion}`);
    }
    if (typeof manifest.path !== 'string' || typeof manifest.sha256 !== 'string') {
      throw new Error('flywheel anchor manifest requires path and sha256');
    }
    // Manifest-relative paths are easier to relocate while remaining
    // repository-contained; project-relative paths remain supported.
    const manifestRelative = resolve(dirname(containedManifest), manifest.path);
    const requested = existsSync(manifestRelative) ? manifestRelative : resolve(root, manifest.path);
    return toSelection(containedPath(root, requested), manifest.sha256);
  }

  if (isRufloRepository(root) || process.env.RUFLO_FLYWHEEL_ALLOW_BUILTIN_ANCHOR === '1') {
    const frozen = loadFrozenHumanEval();
    return {
      version: frozen.version,
      anchorRef: FROZEN_HUMAN_EVAL_HASH,
      source: 'ruflo-built-in',
      tasks: frozen.tasks.map((task) => ({
        id: task.id,
        input: { id: task.id, q: task.q },
        expected: task.labels,

View on GitHub (pinned to 2602b642d9)