{"record":{"id":"569d6314a8840adb","repo":"hexojs/hexo","slug":"category-name-has-already-existed","errorCode":null,"errorMessage":"Category \\`${name}\\` has already existed!","messagePattern":"Category \\\\`(.+?)\\\\` has already existed!","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/models/category.ts","lineNumber":72,"sourceCode":"  Category.virtual('length').get(function() {\n    const ReadOnlyPostCategory = ctx._binaryRelationIndex.post_category;\n\n    return ReadOnlyPostCategory.find({category_id: this._id}).length;\n  });\n\n  // Check whether a category exists\n  Category.pre('save', (data: CategorySchema) => {\n    const { name, parent } = data;\n    if (!name) return;\n\n    const Category = ctx.model('Category');\n    const cat = Category.findOne({\n      name,\n      parent: parent || {$exists: false}\n    }, {lean: true});\n\n    if (cat) {\n      throw new Error(`Category \\`${name}\\` has already existed!`);\n    }\n  });\n\n  // Remove PostCategory references\n  Category.pre('remove', (data: CategorySchema) => {\n    const PostCategory = ctx.model('PostCategory');\n    return PostCategory.remove({category_id: data._id});\n  });\n\n  return Category;\n};\n","sourceCodeStart":54,"sourceCodeEnd":84,"githubUrl":"https://github.com/hexojs/hexo/blob/059cb17494d0632a053c24077f5bcb6ae92acc51/lib/models/category.ts#L54-L84","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nctx.model('Category').insert({ name, parent });\n\n// after\nconst Category = ctx.model('Category');\nconst dup = Category.findOne({ name, parent: parent || { $exists: false } }, { lean: true });\nif (!dup) Category.insert({ name, parent });","handlingStrategy":"validation","validationCode":"const Category = hexo.model('Category');\nconst dup = Category.findOne({ name, parent: parent || { $exists: false } }, { lean: true });\nif (dup) {\n  throw new Error(`Category '${name}' already exists (id=${dup._id})`);\n}\nCategory.insert({ name, parent });","typeGuard":null,"tryCatchPattern":"try {\n  Category.insert({ name, parent });\n} catch (e) {\n  if (/has already existed/.test(e.message)) {\n    return Category.findOne({ name, parent: parent || { $exists: false } }, { lean: true });\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["model","category","duplicate","database","validation"],"backgroundTag":null,"analyzedSha":"059cb17494d0632a053c24077f5bcb6ae92acc51","analyzedAt":"2026-08-12T20:52:05.731Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}