ruvnet/ruflo · error
invalid anchor task id at index
Error message
invalid anchor task id at index ${index} What it means
Each anchor task's `id` must match `^[A-Za-z0-9._-]{1,128}$` — ASCII alphanumerics, dot, underscore, hyphen; at most 128 characters; no whitespace or other symbols. IDs become correlation keys in receipts and lineage records, so the format is strict; the message reports the offending array index so you can find it fast.
Solutions
- Rewrite ids to the allowed alphabet, e.g. `proj-123-fix-login`
- Truncate ids longer than 128 characters while keeping them unique
- Make sure the JSON contains strings, not numbers (`"id": 1` becomes `"id": "1"`)
Example fix
// before
{ "id": "PROJ-123 fix/login", "q": "...", "labels": ["..."] }
// after
{ "id": "PROJ-123-fix-login", "q": "...", "labels": ["..."] } Defensive patterns
Strategy: type-guard
Validate before calling
import { readFileSync } from 'node:fs';
const TASK_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
function anchorTaskIdsOk(file: string): { ok: boolean; badIndex?: number } {
const { tasks } = JSON.parse(readFileSync(file, 'utf8'));
if (!Array.isArray(tasks)) return { ok: false };
for (const [i, t] of tasks.entries()) {
if (!t || typeof t.id !== 'string' || !TASK_ID_RE.test(t.id)) return { ok: false, badIndex: i };
}
return { ok: true };
} Type guard
const TASK_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
interface TaskLike { id?: unknown; q?: unknown; labels?: unknown; }
function hasValidTaskId(task: unknown): task is TaskLike & { id: string } {
return typeof (task as TaskLike)?.id === 'string' && TASK_ID_RE.test((task as TaskLike).id as string);
}
// Usage: filter/flag bad tasks before writing or sending the file
const invalid = tasks.map((t, i) => ({ t, i })).filter(({ t }) => !hasValidTaskId(t)); Try / catch
try {
return loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
if (e instanceof Error && /^invalid anchor task id at index \d+$/.test(e.message)) {
const index = Number(e.message.match(/(\d+)$/)?.[1]);
throw new Error(`Task #${index} has an id outside [A-Za-z0-9._-]{1,128} — slugify it before retrying.`);
}
throw e;
} Prevention
- Slugify ids from ticket systems at generation time (spaces and slashes are the usual culprits)
- Cap ids at 128 chars
- Emit ids as JSON strings, never bare numbers
When it happens
Trigger: Task ids containing spaces, slashes, colons (`task: 1`), unicode characters, ids longer than 128 chars, or non-string ids (bare numbers in JSON) — validation runs per task in file order.
Common situations: Copying ids verbatim from ticket systems ('PROJ-123 fix login'); unicode ids from localization workflows; JSON generated by scripts with numeric primary keys.
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
- anchor task has no query
- anchor task requires non-empty string labels
- duplicate anchor task id
- flywheel anchor manifest requires path and sha256
- project flywheel anchor requires at least 4 labelled tasks
AI-assisted analysis of ruvnet/ruflo@2602b642d9 (2026-08-18).
Data as JSON: /api/errors/75f9cf3a44618b30.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/services/harness-project-anchor.ts:100
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 2602b642d9)