{"record":{"id":"46696087748915a7","repo":"meteor/meteor","slug":"can-t-change-a-doc-s-id-while-updating","errorCode":null,"errorMessage":"Can't change a doc's _id while updating","messagePattern":"Can't change a doc's _id while updating","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/minimongo/local_collection.js","lineNumber":1758,"sourceCode":"};\n\n// Is this selector just shorthand for lookup by _id?\nLocalCollection._selectorIsId = selector =>\n  typeof selector === 'number' ||\n  typeof selector === 'string' ||\n  selector instanceof MongoID.ObjectID\n;\n\n// Is the selector just lookup by _id (shorthand or not)?\nLocalCollection._selectorIsIdPerhapsAsObject = selector =>\n  LocalCollection._selectorIsId(selector) ||\n  LocalCollection._selectorIsId(selector && selector._id) &&\n  Object.keys(selector).length === 1\n;\n\nLocalCollection._updateInResultsSync = (query, doc, old_doc) => {\n  if (!EJSON.equals(doc._id, old_doc._id)) {\n    throw new Error('Can\\'t change a doc\\'s _id while updating');\n  }\n\n  const projectionFn = query.projectionFn;\n  const changedFields = DiffSequence.makeChangedFields(\n    projectionFn(doc),\n    projectionFn(old_doc)\n  );\n\n  if (!query.ordered) {\n    if (Object.keys(changedFields).length) {\n      query.changed(doc._id, changedFields);\n      query.results.set(doc._id, doc);\n    }\n\n    return;\n  }\n\n  const old_idx = LocalCollection._findInOrderedResults(query, doc);","sourceCodeStart":1740,"sourceCodeEnd":1776,"githubUrl":"https://github.com/meteor/meteor/blob/5076d2f818d3cac01d0cf8312fa5b2332076a4fb/packages/minimongo/local_collection.js#L1740-L1776","documentation":"Thrown by LocalCollection._updateInResultsSync when, during an ordered/unordered observe on a local cache, a doc produced by applying a change has an _id that is not EJSON.equals to the previous doc's _id. The observe machinery keys documents by _id; an _id change mid-update would corrupt the result-set index, so it is rejected. This is an internal invariant of the reactive cache, not a direct user-facing API.","triggerScenarios":"A transform function passed to observe mutates the _id; a server 'changed' message includes an _id field that differs from the added doc's _id; a projection that rewrites _id; two different server docs sharing an id collision after a transform. Also reproducible by manually poking the cache or by a buggy publish that sends changed{_id: newId} for an existing doc.","commonSituations":"Custom transform on a cursor that returns a new object whose _id is computed/normalized differently; EJSON id type mismatch (string vs ObjectID vs MongoID.ObjectID) where EJSON.equals returns false even though the values look identical as strings; a publication that mistakenly sends _id in changed fields.","solutions":["Ensure transforms do not alter the _id (return a new object but copy _id verbatim).","Make sure added and changed messages for the same logical doc use EJSON-equal _id values, including matching id type (string vs ObjectID).","Remove _id from any 'changed' fields your publish function emits; _id must be immutable per document.","If you are seeing this on a normal subscription, file a bug against the publish function — the client cache treats _id as immutable."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// For transforms: ensure _id is preserved verbatim.\nfunction safeTransform(fn) {\n  return (doc) => {\n    const out = fn(doc);\n    if (!EJSON.equals(out._id, doc._id)) {\n      throw new TypeError('Transform must not change _id');\n    }\n    return out;\n  };\n}\ncursor.observe({ /* ... */ }, { transform: safeTransform(myTransform) });","tryCatchPattern":null,"preventionTips":["Never include _id in a 'changed' payload on the server side.","Keep id types consistent: pick string OR MongoID.ObjectID for a given collection and stick to it.","Verify any transform copies _id without modification.","Add a fixture that asserts EJSON.equals(before._id, after._id) for every changed message."],"tags":["minimongo","observe","invariant","objectid","reactivity"],"backgroundTag":null,"analyzedSha":"5076d2f818d3cac01d0cf8312fa5b2332076a4fb","analyzedAt":"2026-08-13T03:27:40.142Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}