ruvnet/ruflo · error
anchor task requires non-empty string labels
Error message
anchor task ${task.id} requires non-empty string labels What it means
Every anchor task must have a `labels` array containing at least one non-blank string (every entry checked with trim). Labels are the expected-results ground truth the human-relevance delta is computed from; a missing, empty, or malformed labels array invalidates the whole measurement, so the file is rejected.
Solutions
- Give each task a non-empty array of expected-result strings (e.g. relevant file paths or keywords)
- Wrap bare-string labels in an array: `labels: "src/api.ts"` becomes `labels: ["src/api.ts"]`
- Filter out blank entries when generating the file
Example fix
// before
{ "id": "t1", "q": "...", "labels": "src/api.ts" }
// after
{ "id": "t1", "q": "...", "labels": ["src/api.ts", "src/routes/api.ts"] } Defensive patterns
Strategy: type-guard
Validate before calling
function labelsFilled(tasks: Array<{ id: string; labels?: unknown }>): string[] {
return tasks
.filter((t) => !Array.isArray(t.labels) || t.labels.length === 0 || t.labels.some((l) => typeof l !== 'string' || !l.trim()))
.map((t) => t.id);
} Type guard
interface TaskLike { id?: unknown; q?: unknown; labels?: unknown; }
function hasLabels(task: unknown): task is TaskLike & { labels: string[] } {
const l = (task as TaskLike)?.labels;
return Array.isArray(l) && l.length > 0 && l.every((x) => typeof x === 'string' && x.trim().length > 0);
}
// Usage: const bad = tasks.filter((t) => !hasLabels(t)).map((t) => t.id); Try / catch
try {
return loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
if (e instanceof Error && /anchor task .+ requires non-empty string labels$/.test(e.message)) {
const id = e.message.match(/anchor task (.+) requires/)?.[1];
throw new Error(`Task '${id}' needs labels: ['expected/file.ts', ...] — expected results for the query.`);
}
throw e;
} Prevention
- Always emit labels as an array of non-empty strings, even for a single expected result
- Filter empty CSV cells out of label lists at generation time
- Include label presence in anchor-file linting
When it happens
Trigger: `labels` absent, not an array (`labels: "api"`), an empty array, or an array containing blank or non-string entries such as `["", " "]` or `[1]`.
Common situations: Hand-authored anchors forgetting the labels key; single-label shorthand written as a bare string; CSV exports producing empty cells as empty strings.
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
- flywheel anchor manifest requires path and sha256
- duplicate anchor task id
- invalid anchor task id at index
- project flywheel anchor requires at least 4 labelled tasks
AI-assisted analysis of ruvnet/ruflo@2602b642d9 (2026-08-18).
Data as JSON: /api/errors/70e62f9ff600df4c.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/services/harness-project-anchor.ts:108
};
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);
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',View on GitHub (pinned to 2602b642d9)