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
- Supply both: `anchorPath` plus the `sha256:…` hash of that file's tasks
- Or supply neither and let resolution fall through to the manifest at `.claude/eval/flywheel-anchor.manifest.json` (or the default chain)
- 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
- Model anchorPath/anchorHash as a single optional object in your config, not two independent fields
- Reject half-filled config at load time with a clear message
- Copy invocation examples verbatim — the pair is load-bearing, not incidental
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
- anchor task has no query
- anchor task requires non-empty string labels
- duplicate anchor task id
- flywheel anchor manifest requires path and sha256
- flywheel anchor path must stay inside project root
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)