ruvnet/ruflo · error · Error
anchor task ${task.id} requires non-empty string labels
Error message
anchor task ${task.id} requires non-empty string labels What it means
Thrown per-task when task.labels is missing, not an array, an empty array, or contains any element that is not a non-empty string (after trim). Labels are the expected-relevance markers the flywheel uses to score retrieval, so each task must carry at least one meaningful label. The check is strict: a labels value of ["", " "] fails because every element must be a non-blank string.
Source
Thrown at v3/@claude-flow/cli/src/services/harness-project-anchor.ts:97
};
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 6b01dc5a68)
Solutions
- Ensure labels is an array of at least one non-empty string, e.g. ["auth", "middleware"].
- Filter out null/blank entries when generating labels: labels.filter(l => typeof l === 'string' && l.trim()).
- Verify Array.isArray(task.labels) && task.labels.length > 0 for every task before saving.
Example fix
// before
{"id":"t1","q":"...","labels":"auth"}
// after — wrap in an array, all non-empty strings
{"id":"t1","q":"...","labels":["auth"]} Defensive patterns
Strategy: validation
Validate before calling
function assertLabels(tasks: { id: string; labels?: unknown }[]): void {
for (const t of tasks) {
const labels = t.labels;
if (!Array.isArray(labels) || labels.length === 0
|| labels.some(l => typeof l !== 'string' || !l.trim())) {
throw new Error(`task ${t.id} has invalid labels`);
}
}
} Type guard
function hasValidLabels(task: unknown): boolean {
if (typeof task !== 'object' || task === null) return false;
const labels = (task as { labels?: unknown }).labels;
return Array.isArray(labels) && labels.length > 0
&& labels.every(l => typeof l === 'string' && l.trim().length > 0);
} Try / catch
try {
loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
if (e instanceof Error && /requires non-empty string labels/.test(e.message)) {
const id = e.message.match(/task (.+?) requires/)?.[1];
// fix that task's labels array, then retry
}
throw e;
} Prevention
- Always wrap labels in an array, even for a single label.
- Filter out null/blank labels during generation.
- Assert Array.isArray(labels) && labels.length in your anchor schema tests.
When it happens
Trigger: A task object where labels is undefined, a string instead of an array, [], or an array containing non-string values (numbers, nulls, objects) or blank strings. The some() predicate fails on the first offending element.
Common situations: Using a single label string instead of a one-element array (labels: "auth" vs labels: ["auth"]); an auto-labeller that emitted nulls for unmatched tasks; copying labels from a CSV that introduced empty trailing elements; forgetting the field on tasks added late.
Related errors
- project flywheel anchor requires at least 4 labelled tasks
- invalid anchor task id at index ${index}
- duplicate anchor task id: ${task.id}
- anchor task ${task.id} has no query
- flywheel anchor manifest requires path and sha256
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/70e62f9ff600df4c.
Report an issue: GitHub.