eyaltoledano/claude-task-master · error · Error

Tag "${newName}" already exists

Error message

Tag "${newName}" already exists

What it means

renameTag() refuses to rename to a tag name that already exists in the tagged data, since two tags cannot share one key. This prevents silently overwriting an existing tag's tasks. Delete the conflicting tag or choose another name.

Source

Thrown at scripts/modules/task-manager/tag-management.js:1001

		logFn.info(`Renaming tag from "${oldName}" to "${newName}"`);

		// Read current tasks data
		const data = readJSON(tasksPath, projectRoot);
		if (!data) {
			throw new Error(`Could not read tasks file at ${tasksPath}`);
		}

		// Use raw tagged data for tag operations
		const rawData = data._rawTaggedData || data;

		// Check if old tag exists
		if (!rawData[oldName]) {
			throw new Error(`Tag "${oldName}" does not exist`);
		}

		// Check if new tag name already exists
		if (rawData[newName]) {
			throw new Error(`Tag "${newName}" already exists`);
		}

		// Get current tag to check if we're renaming the active tag
		const currentTag = getCurrentTag(projectRoot);
		const isCurrentTag = currentTag === oldName;

		// Rename the tag by copying data and deleting old
		rawData[newName] = { ...rawData[oldName] };

		// Update metadata if it exists
		if (rawData[newName].metadata) {
			rawData[newName].metadata.renamed = {
				from: oldName,
				date: new Date().toISOString()
			};
		}

		delete rawData[oldName];

View on GitHub (pinned to c0c98d367c)

Solutions

  1. Choose a unique target name that does not already exist in tasks.json
  2. Delete or rename the existing conflicting tag first (deleteTag / another renameTag call)
  3. Guard before calling: check the target key is absent in the parsed tasks.json data
  4. If data is left over from a failed rename, clean up the duplicate tag manually in tasks.json

Example fix

// before
await renameTag(tasksPath, 'wip', 'in-progress'); // already exists
// after
const data = JSON.parse(fs.readFileSync(tasksPath, 'utf8'));
if (data['in-progress']) await deleteTag(tasksPath, 'in-progress');
await renameTag(tasksPath, 'wip', 'in-progress');
Defensive patterns

Strategy: validation

Validate before calling

const data = JSON.parse(require('fs').readFileSync(tasksPath, 'utf8'));
if (Object.prototype.hasOwnProperty.call(data, newName)) {
  throw new Error(`Target tag "${newName}" already exists; choose another name or delete it first`);
}

Type guard

function tagIsFree(tasksData, name) {
  return typeof name === 'string' && !Object.prototype.hasOwnProperty.call(tasksData, name);
}

Try / catch

try {
  await renameTag(tasksPath, oldName, newName);
} catch (err) {
  if (err.message.includes('already exists')) {
    console.error(`Tag "${newName}" is taken — pick a unique name or delete the existing tag`);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling renameTag(tasksPath, 'a', 'b') when a tag 'b' already exists; renaming a tag back to a name it previously held; bulk rename scripts producing colliding targets.

Common situations: Retry after a partially completed rename left both names present; users toggling between two names ('todo' <-> 'doing') without deleting; auto-generated names colliding with existing tags.

Related errors


AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29). Data as JSON: /api/errors/d2e8d14dc236af5d. Report an issue: GitHub.