ruvnet/ruflo · error

anchorPath and anchorHash must be supplied together

Error message

anchorPath and anchorHash must be supplied together

What it means

`loadEffectiveFlywheelAnchor` treats `anchorPath` and `anchorHash` as a mandatory pair: supplying exactly one of them throws (`!!anchorPath !== !!anchorHash`). The rule exists because a path without a hash would load an *unverified* anchor — every explicitly selected anchor must be content-pinned, which is the whole point of #2840's fail-closed design.

Solutions

  1. Supply both: `anchorPath` plus the `sha256:…` hash of that file's tasks
  2. Or supply neither and let resolution fall through to the manifest at `.claude/eval/flywheel-anchor.manifest.json` (or the default chain)
  3. Compute the hash with the exported `humanEvalHash(tasks)` helper instead of hand-crafting it

Example fix

// before
loadEffectiveFlywheelAnchor(root, { anchorPath: 'eval/tasks.json' });

// after: path and hash always travel together
loadEffectiveFlywheelAnchor(root, {
  anchorPath: 'eval/tasks.json',
  anchorHash: humanEvalHash(tasks), // 'sha256:<64 hex>'
});
Defensive patterns

Strategy: validation

Validate before calling

function anchorOptionsPaired(opts: { anchorPath?: string; anchorHash?: string }): boolean {
  return opts.anchorPath === undefined ? opts.anchorHash === undefined : opts.anchorHash !== undefined;
}

if (!anchorOptionsPaired(opts)) {
  throw new Error('anchorPath and anchorHash must be supplied together (or both omitted)');
}

Type guard

interface AnchorOptions { anchorPath?: string; anchorHash?: string; manifestPath?: string; }

function isPairedAnchorOptions(
  opts: AnchorOptions,
): opts is AnchorOptions & ({ anchorPath: string; anchorHash: string } | { anchorPath?: undefined; anchorHash?: undefined }) {
  return opts.anchorPath === undefined ? opts.anchorHash === undefined : opts.anchorHash !== undefined;
}

Try / catch

try {
  return loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
  if (e?.message === 'anchorPath and anchorHash must be supplied together') {
    // either add the missing half (compute it with humanEvalHash) or drop both
    throw new Error('Pass both anchorPath and anchorHash, or neither (manifest resolution).');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `loadEffectiveFlywheelAnchor(root, { anchorPath: '.claude/eval/tasks.json' })` without `anchorHash`, or passing only a hash (e.g. a config where the path lives in another field).

Common situations: Config schemas modelling path and hash as independent optional fields; partial config merges dropping one key; copy-pasting half of an example invocation.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

    const pkg = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8')) as {
      name?: string;
      repository?: string | { url?: string };
    };
    const repository = typeof pkg.repository === 'string' ? pkg.repository : pkg.repository?.url;
    return ['claude-flow', 'ruflo', '@claude-flow/cli'].includes(pkg.name ?? '')
      && /github\.com[/:]ruvnet\/(?:ruflo|claude-flow)(?:\.git)?$/i.test(repository ?? '');
  } catch {
    return false;
  }
}

export function loadEffectiveFlywheelAnchor(
  projectRoot: string,
  options: LoadFlywheelAnchorOptions = {},
): FlywheelAnchorSelection {
  const root = resolve(projectRoot);
  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');
    }

View on GitHub (pinned to 2602b642d9)