hexojs/hexo · error · Error

Tag \`${name}\` has already existed!

Error message

Tag \`${name}\` has already existed!

What it means

Thrown by the Tag model pre('save') hook (lib/models/tag.ts:62). Before a Tag document is saved, the hook queries for an existing tag with the same name; if found, the save is aborted to prevent duplicate tags.

Source

Thrown at lib/models/tag.ts:62

  Tag.virtual('length').get(function() {
    // Note: this.posts.length is also working
    // But it's slow because `find` has to iterate over all posts
    const ReadOnlyPostTag = ctx._binaryRelationIndex.post_tag;

    return ReadOnlyPostTag.find({tag_id: this._id}).length;
  });

  // Check whether a tag exists
  Tag.pre('save', (data: TagSchema) => {
    const { name } = data;
    if (!name) return;

    const Tag = ctx.model('Tag');
    const tag = Tag.findOne({name}, {lean: true});

    if (tag) {
      throw new Error(`Tag \`${name}\` has already existed!`);
    }
  });

  // Remove PostTag references
  Tag.pre('remove', (data: TagSchema) => {
    const PostTag = ctx.model('PostTag');
    return PostTag.remove({tag_id: data._id});
  });

  return Tag;
};

View on GitHub (pinned to 059cb17494)

Solutions

  1. Before insert, query: const exists = ctx.model('Tag').findOne({ name }, { lean: true }); reuse if exists.
  2. Rely on front-matter tag processing which dedupes by name.
  3. Run hexo clean before re-importing to reset the tag store.
  4. Link posts to the existing tag instead of creating a duplicate.

Example fix

// before
ctx.model('Tag').insert({ name });

// after
const Tag = ctx.model('Tag');
const dup = Tag.findOne({ name }, { lean: true });
if (!dup) Tag.insert({ name });
Defensive patterns

Strategy: validation

Validate before calling

const Tag = hexo.model('Tag');
const dup = Tag.findOne({ name }, { lean: true });
if (dup) {
  throw new Error(`Tag '${name}' already exists (id=${dup._id})`);
}
Tag.insert({ name });

Try / catch

try {
  Tag.insert({ name });
} catch (e) {
  if (/has already existed/.test(e.message)) {
    return Tag.findOne({ name }, { lean: true });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling ctx.model('Tag').insert({ name: 'js' }) when a Tag named 'js' already exists; re-importing posts whose front-matter tags collide; a plugin inserting tags without checking.

Common situations: Running an import twice without hexo clean; programmatic tag creation that bypasses the dedup logic; case variants that slugize to the same name but differ in stored name; stale db.json.

Related errors


AI-assisted analysis of hexojs/hexo@059cb17494 (2026-08-12). Data as JSON: /api/errors/7365e91e1d2f88fd. Report an issue: GitHub.