ruvnet/ruflo · error · Error

anchor task ${task.id} has no query

Error message

anchor task ${task.id} has no query

What it means

Thrown per-task when task.q is missing, not a string, or trims to an empty string. The 'q' field is the retrieval query that the flywheel harness evaluates against; an empty query makes the task unmeasurable. The trim check rejects whitespace-only queries, not just literal empty strings.

Source

Thrown at v3/@claude-flow/cli/src/services/harness-project-anchor.ts:94

    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);
  const actualHash = humanEvalHash(parsed.tasks);
  if (actualHash !== normalizeHash(expectedHash)) {
    throw new Error(`project flywheel anchor hash mismatch (got ${actualHash}, pinned ${normalizeHash(expectedHash)})`);
  }
  return {

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Find the task by the id in the message and populate its q field with a non-empty query string.
  2. Confirm the key is the single letter "q" (not "query", "prompt", "question").
  3. Add a generation-time check that every task has a non-blank q before emitting the anchor.

Example fix

// before
{"id":"search-1","q":"   ","labels":["retrieval"]}
// after
{"id":"search-1","q":"How does the auth middleware refresh expired tokens?","labels":["retrieval"]}
Defensive patterns

Strategy: validation

Validate before calling

function assertQueries(tasks: { id: string; q?: unknown }[]): void {
  for (const t of tasks) {
    if (typeof t.q !== 'string' || t.q.trim().length === 0) {
      throw new Error(`task ${t.id} has no/blank query`);
    }
  }
}

Type guard

function hasQuery(task: unknown): task is { q: string } {
  return typeof task === 'object' && task !== null
    && typeof (task as { q?: unknown }).q === 'string'
    && (task as { q: string }).q.trim().length > 0;
}

Try / catch

try {
  loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
  if (e instanceof Error && /has no query/.test(e.message)) {
    const id = e.message.match(/task (.+?) has no query/)?.[1];
    // populate the q field for that task, then retry
  }
  throw e;
}

Prevention

When it happens

Trigger: A task object whose q field is undefined, null, a number, an empty string "", or a string of only spaces/tabs/newlines. Also fires when the key is mistyped (e.g. "query" instead of "q").

Common situations: Authoring a task skeleton with a placeholder query like "" or "TODO"-and-then-deleting it; renaming the field to a more descriptive 'query'/'prompt' key; a templating step that left q blank for tasks whose data hadn't been filled in.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/80c663ef71d995e3. Report an issue: GitHub.