hexojs/hexo · error · Error
Category \`${name}\` has already existed!
Error message
Category \`${name}\` has already existed! What it means
Thrown by the Category model pre('save') hook (lib/models/category.ts:72). Before a Category document is saved, the hook queries for an existing category with the same name and the same parent (or no parent). If one already exists, the save is aborted to prevent duplicates.
Source
Thrown at lib/models/category.ts:72
Category.virtual('length').get(function() {
const ReadOnlyPostCategory = ctx._binaryRelationIndex.post_category;
return ReadOnlyPostCategory.find({category_id: this._id}).length;
});
// Check whether a category exists
Category.pre('save', (data: CategorySchema) => {
const { name, parent } = data;
if (!name) return;
const Category = ctx.model('Category');
const cat = Category.findOne({
name,
parent: parent || {$exists: false}
}, {lean: true});
if (cat) {
throw new Error(`Category \`${name}\` has already existed!`);
}
});
// Remove PostCategory references
Category.pre('remove', (data: CategorySchema) => {
const PostCategory = ctx.model('PostCategory');
return PostCategory.remove({category_id: data._id});
});
return Category;
};
View on GitHub (pinned to 059cb17494)
Solutions
- Before insert, query: const exists = ctx.model('Category').findOne({ name, parent: parent || { $exists: false } }, { lean: true }); skip if exists.
- Use the post category helper / front-matter flow which dedupes automatically.
- Clear the database (hexo clean) before re-importing.
- Make category names unique or merge into the existing one instead of creating a new one.
Example fix
// before
ctx.model('Category').insert({ name, parent });
// after
const Category = ctx.model('Category');
const dup = Category.findOne({ name, parent: parent || { $exists: false } }, { lean: true });
if (!dup) Category.insert({ name, parent }); Defensive patterns
Strategy: validation
Validate before calling
const Category = hexo.model('Category');
const dup = Category.findOne({ name, parent: parent || { $exists: false } }, { lean: true });
if (dup) {
throw new Error(`Category '${name}' already exists (id=${dup._id})`);
}
Category.insert({ name, parent }); Try / catch
try {
Category.insert({ name, parent });
} catch (e) {
if (/has already existed/.test(e.message)) {
return Category.findOne({ name, parent: parent || { $exists: false } }, { lean: true });
}
throw e;
} Prevention
- Run hexo clean before bulk imports to reset the store.
- Prefer front-matter category flow which dedupes automatically.
- Wrap programmatic inserts with a findOne pre-check.
When it happens
Trigger: Calling ctx.model('Category').insert({ name: 'Foo', parent: ... }) when a Category row with name 'Foo' and the same parent already exists; re-importing posts whose front-matter categories collide; a plugin inserting categories without dedup.
Common situations: Running an import twice without clearing the database; two posts defining the same category with a parent that already registered; programmatic category creation in a generator that does not check first; stale db.json from a previous run.
Related errors
- Tag \`${name}\` has already existed!
- `${value}` is not a valid date!
- fn must be a function
- name is required
- fn must be a function
AI-assisted analysis of hexojs/hexo@059cb17494 (2026-08-12).
Data as JSON: /api/errors/569d6314a8840adb.
Report an issue: GitHub.