{"record":{"id":"75f9cf3a44618b30","repo":"ruvnet/ruflo","slug":"invalid-anchor-task-id-at-index-index","errorCode":null,"errorMessage":"invalid anchor task id at index ${index}","messagePattern":"invalid anchor task id at index (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/harness-project-anchor.ts","lineNumber":89,"sourceCode":"  return actual;\n}\n\nfunction parseTasks(path: string): { version: string; tasks: HumanEvalTask[] } {\n  const parsed = JSON.parse(readFileSync(path, 'utf8')) as {\n    schemaVersion?: string;\n    version?: string;\n    tasks?: HumanEvalTask[];\n  };\n  if (parsed.schemaVersion && parsed.schemaVersion !== PROJECT_ANCHOR_SCHEMA) {\n    throw new Error(`unsupported flywheel anchor schema: ${parsed.schemaVersion}`);\n  }\n  if (!Array.isArray(parsed.tasks) || parsed.tasks.length < 4) {\n    throw new Error('project flywheel anchor requires at least 4 labelled tasks');\n  }\n  const ids = new Set<string>();\n  for (const [index, task] of parsed.tasks.entries()) {\n    if (!task || typeof task.id !== 'string' || !/^[A-Za-z0-9._-]{1,128}$/.test(task.id)) {\n      throw new Error(`invalid anchor task id at index ${index}`);\n    }\n    if (ids.has(task.id)) throw new Error(`duplicate anchor task id: ${task.id}`);\n    ids.add(task.id);\n    if (typeof task.q !== 'string' || task.q.trim().length === 0) {\n      throw new Error(`anchor task ${task.id} has no query`);\n    }\n    if (!Array.isArray(task.labels) || task.labels.length === 0 || task.labels.some((label) => typeof label !== 'string' || !label.trim())) {\n      throw new Error(`anchor task ${task.id} requires non-empty string labels`);\n    }\n  }\n  return { version: parsed.version ?? 'project-anchor-v1', tasks: parsed.tasks };\n}\n\nfunction toSelection(\n  path: string,\n  expectedHash: string,\n): FlywheelAnchorSelection {\n  const parsed = parseTasks(path);","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/services/harness-project-anchor.ts#L71-L107","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n{\"id\": \"auth/login flow\", \"q\": \"...\", \"labels\": [\"security\"]}\n// after — sanitize to allowed charset\n{\"id\": \"auth-login-flow\", \"q\": \"...\", \"labels\": [\"security\"]}","handlingStrategy":"validation","validationCode":"const ID_RE = /^[A-Za-z0-9._-]{1,128}$/;\nfunction assertTaskIds(tasks: { id?: unknown }[]): void {\n  tasks.forEach((t, i) => {\n    if (typeof t.id !== 'string' || !ID_RE.test(t.id)) {\n      throw new Error(`task at index ${i} has invalid id: ${JSON.stringify(t.id)}`);\n    }\n  });\n}","typeGuard":"const ID_RE = /^[A-Za-z0-9._-]{1,128}$/;\nfunction hasValidTaskId(task: unknown): task is { id: string } {\n  return typeof task === 'object' && task !== null\n    && typeof (task as { id?: unknown }).id === 'string'\n    && ID_RE.test((task as { id: string }).id);\n}","tryCatchPattern":"try {\n  loadEffectiveFlywheelAnchor(root, opts);\n} catch (e) {\n  if (e instanceof Error && /invalid anchor task id at index (\\d+)/.test(e.message)) {\n    const idx = Number(e.message.match(/index (\\d+)/)?.[1]);\n    // fix tasks[idx].id to match the charset, then retry\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["flywheel-anchor","validation","identifiers","regex","json"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}