ruvnet/ruflo · error · Error
invalid anchor task id at index ${index}
Error message
invalid anchor task id at index ${index} What it means
Thrown per-task during anchor parsing when a task's id is missing, not a string, or fails the regex ^[A-Za-z0-9._-]{1,128}$. The id must be a non-empty ASCII identifier (1-128 chars) drawn from letters, digits, dots, underscores, and hyphens — no spaces, slashes, colons, or unicode. The index in the message is the array position, making the offending task easy to locate.
Source
Thrown at v3/@claude-flow/cli/src/services/harness-project-anchor.ts:89
return actual;
}
function parseTasks(path: string): { version: string; tasks: HumanEvalTask[] } {
const parsed = JSON.parse(readFileSync(path, 'utf8')) as {
schemaVersion?: string;
version?: string;
tasks?: HumanEvalTask[];
};
if (parsed.schemaVersion && parsed.schemaVersion !== PROJECT_ANCHOR_SCHEMA) {
throw new Error(`unsupported flywheel anchor schema: ${parsed.schemaVersion}`);
}
if (!Array.isArray(parsed.tasks) || parsed.tasks.length < 4) {
throw new Error('project flywheel anchor requires at least 4 labelled tasks');
}
const ids = new Set<string>();
for (const [index, task] of parsed.tasks.entries()) {
if (!task || typeof task.id !== 'string' || !/^[A-Za-z0-9._-]{1,128}$/.test(task.id)) {
throw new Error(`invalid anchor task id at index ${index}`);
}
if (ids.has(task.id)) throw new Error(`duplicate anchor task id: ${task.id}`);
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);View on GitHub (pinned to 6b01dc5a68)
Solutions
- Inspect the task at the reported array index and replace its id with a string matching ^[A-Za-z0-9._-]{1,128}$ (e.g. kebab-case or a UUID).
- If ids are auto-generated, sanitize them by replacing disallowed characters with '-' and truncating to 128 chars.
- Add a pre-flight assertion in your anchor generator that every id matches the regex before writing the file.
Example fix
// before
{"id": "auth/login flow", "q": "...", "labels": ["security"]}
// after — sanitize to allowed charset
{"id": "auth-login-flow", "q": "...", "labels": ["security"]} Defensive patterns
Strategy: validation
Validate before calling
const ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
function assertTaskIds(tasks: { id?: unknown }[]): void {
tasks.forEach((t, i) => {
if (typeof t.id !== 'string' || !ID_RE.test(t.id)) {
throw new Error(`task at index ${i} has invalid id: ${JSON.stringify(t.id)}`);
}
});
} Type guard
const ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
function hasValidTaskId(task: unknown): task is { id: string } {
return typeof task === 'object' && task !== null
&& typeof (task as { id?: unknown }).id === 'string'
&& ID_RE.test((task as { id: string }).id);
} Try / catch
try {
loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
if (e instanceof Error && /invalid anchor task id at index (\d+)/.test(e.message)) {
const idx = Number(e.message.match(/index (\d+)/)?.[1]);
// fix tasks[idx].id to match the charset, then retry
}
throw e;
} Prevention
- Use kebab-case or UUIDs for task ids.
- Sanitize generated ids by replacing disallowed chars with '-' and truncating to 128.
- Add a generator test asserting every id matches the regex.
When it happens
Trigger: An anchor task object whose id field is undefined/number/object, an empty string, contains whitespace or path separators, exceeds 128 characters, or includes disallowed characters like '/', ':', '@', '#'. A blank-string id also triggers this since the regex requires at least one character.
Common situations: Using a UUID with hyphens is fine, but copying a human-readable title with spaces into the id field; using a file path or URL fragment as an id; generating ids from timestamps with ':' separators; truncating or mangling ids during a serialization round-trip.
Related errors
- project flywheel anchor requires at least 4 labelled tasks
- duplicate anchor task id: ${task.id}
- anchor task ${task.id} has no query
- anchor task ${task.id} requires non-empty string labels
- flywheel anchor manifest requires path and sha256
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/75f9cf3a44618b30.
Report an issue: GitHub.