eyaltoledano/claude-task-master · error · TaskMasterError
VALIDATION_ERROR
VALIDATION_ERROR
Error message
Tag ${tagName} already exists What it means
createTag refuses to create a tag whose name is already a key in the legacy-format tasks.json. It throws a TaskMasterError with code VALIDATION_ERROR so callers can distinguish it from generic failures. Tags are unique namespaces for task sets, so duplicate creation is treated as a validation problem, not a filesystem problem.
Source
Thrown at packages/tm-core/src/modules/storage/adapters/file-storage/file-storage.ts:632
/**
* Create a new tag in the tasks.json file
* Uses modifyJson for atomic read-modify-write to prevent lost updates
*/
async createTag(
tagName: string,
options?: { copyFrom?: string; description?: string }
): Promise<void> {
const filePath = this.pathResolver.getTasksPath();
try {
await this.fileOps.modifyJson(filePath, (existingData: any) => {
const format = this.formatHandler.detectFormat(existingData);
if (format === 'legacy') {
// Legacy format - add new tag key
if (tagName in existingData) {
throw new TaskMasterError(
`Tag ${tagName} already exists`,
ERROR_CODES.VALIDATION_ERROR
);
}
// Get tasks to copy if specified
let tasksToCopy: any[] = [];
if (options?.copyFrom) {
if (
options.copyFrom in existingData &&
existingData[options.copyFrom].tasks
) {
tasksToCopy = JSON.parse(
JSON.stringify(existingData[options.copyFrom].tasks)
);
}
}
View on GitHub (pinned to c0c98d367c)
Solutions
- List existing tags first (storage.getTagsWithStats()) and skip creation if the tag already exists.
- Catch the TaskMasterError and check error.code === 'VALIDATION_ERROR' to treat duplicates as a no-op.
- Use renameTag if the intent was to change an existing tag rather than create a new one.
- Use copyTag from an existing tag if you wanted the tasks duplicated under a new name.
Example fix
// before
await storage.createTag('feature-x');
// after
const { tags } = await storage.getTagsWithStats();
if (!tags.some((t) => t.name === 'feature-x')) {
await storage.createTag('feature-x');
} Defensive patterns
Strategy: try-catch
Validate before calling
const { tags } = await storage.getTagsWithStats();
if (tags.some((t) => t.name === tagName)) {
return; // already exists, skip creation
} Type guard
function tagExists(tagList: Array<{ name: string }>, name: string): tagList is Array<{ name: string }> & { found: true } {
return tagList.some((t) => t.name === name);
} Try / catch
import { TaskMasterError, ERROR_CODES } from '@tm/core';
try {
await storage.createTag(tagName);
} catch (err) {
if (err instanceof TaskMasterError && err.code === ERROR_CODES.VALIDATION_ERROR) {
return; // duplicate tag — treat as success
}
throw err;
} Prevention
- Make tag provisioning idempotent: check-then-create or catch VALIDATION_ERROR as a no-op.
- List tags before creating in CI pipelines that run on every build.
- Coordinate tag creation across team members/branches to avoid collisions.
- Normalize tag names (trim, consistent casing) before passing to createTag.
When it happens
Trigger: storage.createTag('feature-x') when 'feature-x' already exists in tasks.json; re-running an idempotent-looking setup script twice; calling createTag with 'master' when the file already has a master key; a tag created by another process between your existence check and the create.
Common situations: CI pipelines that provision tags on every run without checking existence; team members both creating the same named tag; scripts that assume create is upsert; copy-pasted tag names differing only by case or whitespace confusion.
Related errors
- MFA_VERIFICATION_FAILED
- Failed to initialize services: ${(error as Error).message}
- Invalid format: ${options.format}. Valid formats are: text,
- Failed to fetch tasks from any tag. First error: ${failedTag
- ${authCheck.error}
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/fb457c3411e28698.
Report an issue: GitHub.