{"record":{"id":"006e68f564067d98","repo":"Automattic/mongoose","slug":"attempting-to-remove-virtual-virtual-that-doe","errorCode":null,"errorMessage":"Attempting to remove virtual \"${virtual}\" that does not exist.","messagePattern":"Attempting to remove virtual \"(.+?)\" that does not exist\\.","errorType":"exception","errorClass":"MongooseError","httpStatus":null,"severity":"error","filePath":"lib/schema.js","lineNumber":2831,"sourceCode":"  delete branch[last];\n}\n\n/**\n * Removes the given virtual or virtuals from the schema.\n *\n * @param {string|Array} path The virutal path(s) to remove.\n * @returns {Schema} the Schema instance, or a mongoose error if the virtual does not exist.\n * @api public\n */\n\nSchema.prototype.removeVirtual = function(path) {\n  if (typeof path === 'string') {\n    path = [path];\n  }\n  if (Array.isArray(path)) {\n    for (const virtual of path) {\n      if (this.virtuals[virtual] == null) {\n        throw new MongooseError(`Attempting to remove virtual \"${virtual}\" that does not exist.`);\n      }\n    }\n\n    for (const virtual of path) {\n      delete this.paths[virtual];\n      delete this.virtuals[virtual];\n      if (virtual.indexOf('.') !== -1) {\n        mpath.unset(virtual, this.tree);\n      } else {\n        delete this.tree[virtual];\n      }\n    }\n  }\n  return this;\n};\n\n/**\n * Loads an ES6 class into a schema. Maps [setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) + [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get), [static methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static),","sourceCodeStart":2813,"sourceCodeEnd":2849,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/schema.js#L2813-L2849","documentation":"removeVirtual only deletes names present in schema.virtuals, which stores exact dotted names as they were defined. Passing a name that was never registered (or is already removed, or sits at a different nesting depth) throws instead of silently succeeding, to surface cleanup mistakes.","triggerScenarios":"`schema.removeVirtual('name.full')` when the virtual was defined as 'fullName'; removing the same virtual twice; plugins removing virtuals that optional configuration never created.","commonSituations":"Conditional schemas (feature flags) where a virtual may or may not exist; refactors renaming virtuals without updating teardown code; loops calling removeVirtual over a stale hardcoded list.","solutions":["Inspect registered names first: `console.log(Object.keys(schema.virtuals))` and use the exact key.","Guard removals: `if (schema.virtuals[name] != null) schema.removeVirtual(name)`.","Build removal lists from Object.keys(schema.virtuals).filter(...) rather than duplicating names in config."],"exampleFix":"// before\nschema.removeVirtual('name.full'); // was actually defined as 'fullName'\n\n// after\nconst target = Object.keys(schema.virtuals).find(n => n === 'fullName');\nif (target) schema.removeVirtual(target);","handlingStrategy":"validation","validationCode":"const removeVirtualIfExists = (schema, name) => {\n  if (schema.virtuals[name] != null) schema.removeVirtual(name);\n};\n// or derive the list dynamically:\nfor (const name of Object.keys(schema.virtuals).filter(n => n.startsWith('tmp'))) {\n  schema.removeVirtual(name);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Derive removal lists from schema.virtuals at runtime instead of duplicating names in config.","Group virtual definitions and their removals in one module so they stay consistent.","Remember names are exact dotted strings - 'a.b' and 'aB' are different virtuals."],"tags":["virtuals","cleanup","api-misuse"],"backgroundTag":"remove-missing-virtual","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}