ruvnet/ruflo · error · Error
project flywheel anchor hash mismatch (got ${actualHash}, pi
Error message
project flywheel anchor hash mismatch (got ${actualHash}, pinned ${normalizeHash(expectedHash)}) What it means
Thrown by toSelection() after parseTasks() succeeds: the sha256 computed by humanEvalHash() over the parsed tasks does not equal the pinned expected hash (after normalizeHash lowercases and adds the 'sha256:' prefix). This is the core tamper/integrity check of project-local anchors — it guarantees the tasks being benchmarked are byte-for-byte the ones that were pinned, so a silent edit (added/removed/changed task, reordered labels, reworded query) cannot pass as the canonical anchor.
Source
Thrown at v3/@claude-flow/cli/src/services/harness-project-anchor.ts:110
ids.add(task.id);
if (typeof task.q !== 'string' || task.q.trim().length === 0) {
throw new Error(`anchor task ${task.id} has no query`);
}
if (!Array.isArray(task.labels) || task.labels.length === 0 || task.labels.some((label) => typeof label !== 'string' || !label.trim())) {
throw new Error(`anchor task ${task.id} requires non-empty string labels`);
}
}
return { version: parsed.version ?? 'project-anchor-v1', tasks: parsed.tasks };
}
function toSelection(
path: string,
expectedHash: string,
): FlywheelAnchorSelection {
const parsed = parseTasks(path);
const actualHash = humanEvalHash(parsed.tasks);
if (actualHash !== normalizeHash(expectedHash)) {
throw new Error(`project flywheel anchor hash mismatch (got ${actualHash}, pinned ${normalizeHash(expectedHash)})`);
}
return {
version: parsed.version,
anchorRef: actualHash,
source: 'project',
path,
tasks: parsed.tasks.map((task) => ({
id: task.id,
input: { id: task.id, q: task.q },
expected: task.labels,
})),
};
}
function isRufloRepository(projectRoot: string): boolean {
try {
const pkg = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8')) as {
name?: string;View on GitHub (pinned to 6b01dc5a68)
Solutions
- Recompute the hash over the current tasks using the same humanEvalHash() function and update the pinned sha256 in the manifest or the anchorHash argument.
- If the edit was unintended, revert the anchor file to the version whose hash matches the pin.
- Confirm you are hashing the exact parsed-task array shape (id+q+labels) that humanEvalHash expects, not a re-serialized string with different key ordering.
Example fix
// before — manifest pins a sha256 for the old tasks
{"path":"anchor.json","sha256":"sha256:aaaa..."}
// after editing anchor.json, recompute and re-pin
import { humanEvalHash } from './harness-frozen-eval.js';
const tasks = JSON.parse(fs.readFileSync('anchor.json','utf8')).tasks;
const newHash = humanEvalHash(tasks); // e.g. 'sha256:bbbb...'
// write newHash into the manifest's sha256 field Defensive patterns
Strategy: validation
Validate before calling
import { humanEvalHash } from './harness-frozen-eval.js';
import { readFileSync } from 'fs';
function assertAnchorHash(tasksPath: string, pinned: string): void {
const tasks = JSON.parse(readFileSync(tasksPath, 'utf8')).tasks;
const actual = humanEvalHash(tasks);
const want = pinned.trim().toLowerCase().startsWith('sha256:') ? pinned.trim().toLowerCase() : `sha256:${pinned.trim().toLowerCase()}`;
if (actual !== want) {
throw new Error(`anchor hash drift: ${actual} != ${want}; re-pin or revert tasks`);
}
} Try / catch
try {
loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
if (e instanceof Error && /hash mismatch/.test(e.message)) {
// either revert tasks to the pinned version, or recompute humanEvalHash(tasks)
// and update the manifest's sha256 / the anchorHash argument
}
throw e;
} Prevention
- Treat the pinned sha256 as load-bearing: any task edit requires re-hashing.
- Store both tasks and manifest in the same commit so they never drift.
- Add a CI check that recomputes humanEvalHash and compares to the manifest's sha256.
When it happens
Trigger: Editing any field of an already-pinned anchor task (id, q, labels) or changing the number/order of tasks without recomputing and updating the pinned sha256. The hash comes from the manifest's sha256 field, or from the anchorHash option passed to loadEffectiveFlywheelAnchor.
Common situations: Fixing a typo in a task query after the anchor was pinned; adding a 5th task to a 4-task anchor; reordering labels within a task (humanEvalHash is order-sensitive); updating ruvllm/claude-flow which changed the hash algorithm; copying an anchor + manifest pair from another repo whose tasks differ but reusing a stale sha256.
Related errors
- KV cache file missing SHA256 footer
- KV cache integrity check failed: hash mismatch
- unsupported flywheel anchor schema: ${parsed.schemaVersion}
- project flywheel anchor requires at least 4 labelled tasks
- anchor task ${task.id} has no query
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/5c3e3781cfb0b5ca.
Report an issue: GitHub.