{"record":{"id":"b10820aa771e902d","repo":"pubkey/rxdb","slug":"doc9","errorCode":"DOC9","errorMessage":"\n        RxDB Error-Code: ${message}.\n        Hint: Error messages are not included in RxDB core to reduce build size.\n        To show the full error messages and to ensure that you do not make any mistakes when using RxDB,\n        use the dev-mode plugin when you are in development mode: https://rxdb.info/dev-mode.html?console=error\n        \nFind out more about this error here: https://rxdb.info/errors.html?console=errors#DOC9 \nStill stuck? Ask in the RxDB Discord: https://rxdb.info/chat \n","messagePattern":"\n        RxDB Error-Code: (.+?)\\.\n        Hint: Error messages are not included in RxDB core to reduce build size\\.\n        To show the full error messages and to ensure that you do not make any mistakes when using RxDB,\n        use the dev-mode plugin when you are in development mode: https://rxdb\\.info/dev-mode\\.html\\?console=error\n        \nFind out more about this error here: https://rxdb\\.info/errors\\.html\\?console=errors#DOC9 \nStill stuck\\? Ask in the RxDB Discord: https://rxdb\\.info/chat \n","errorType":"error_code","errorClass":"RxError","httpStatus":null,"severity":"error","filePath":"src/rx-schema.ts","lineNumber":122,"sourceCode":"     */\n    public get hash(): Promise<string> {\n        return overwriteGetterForCaching(\n            this,\n            'hash',\n            this.hashFunction(JSON.stringify(this.jsonSchema))\n        );\n    }\n\n    /**\n     * checks if a given change on a document is allowed\n     * Ensures that:\n     * - final fields are not modified\n     * @throws {Error} if not valid\n     */\n    validateChange(dataBefore: any, dataAfter: any): void {\n        this.finalFields.forEach(fieldName => {\n            if (!deepEqual(dataBefore[fieldName], dataAfter[fieldName])) {\n                throw newRxError('DOC9', {\n                    dataBefore,\n                    dataAfter,\n                    fieldName,\n                    schema: this.jsonSchema\n                });\n            }\n        });\n    }\n\n    /**\n     * creates the schema-based document-prototype,\n     * see RxCollection.getDocumentPrototype()\n     */\n    public getDocumentPrototype(): any {\n        const proto: any = {};\n\n        /**\n         * On the top level, we know all keys","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/pubkey/rxdb/blob/af6fb65f94cd70558d74c23799eda11ff1c15224/src/rx-schema.ts#L104-L140","documentation":"DOC9 is thrown by RxSchema.validateChange() when a document update modifies a field marked as final in the schema. Final fields are immutable after the document is created; this protects primary keys and other frozen values from diverging between replicas. The error carries dataBefore, dataAfter and the offending fieldName.","triggerScenarios":"Calling doc.incrementalPatch()/incrementalModify()/update() or collection.upsert() with new data in which a final field (including the primary key and fields listed in schema.finalFields) has a value different from the stored document. deepEqual comparison fails, so even changed nested objects in a final field trigger it.","commonSituations":"Trying to change the primary key of an existing document; accidentally including final fields in a patch object built from spread state; upserting a full document where an immutable field was recalculated (e.g. timestamps or counters marked final); migrated code from databases without immutability rules.","solutions":["Remove the final field from your update data; only patch mutable fields.","Use incrementalModify()/incrementalPatch with a modifier that leaves final fields untouched.","If the field genuinely must change, remove 'final' from the schema (requires schema version bump and a migration strategy).","To 'change' an immutable document, remove it and re-insert a new document with a new primary key."],"exampleFix":"// before\nawait doc.incrementalPatch({ id: 'new-id', counter: 5 }); // id is final\n// after\nawait doc.incrementalPatch({ counter: 5 }); // only mutable fields","handlingStrategy":"try-catch","validationCode":"function patchIsSafe(doc: RxDocument, patch: Record<string, any>): boolean {\n  const finalFields: string[] = (doc.collection.schema as any).finalFields;\n  return !finalFields.some(f =>\n    f in patch && !deepEqual((doc.toJSON() as any)[f], patch[f]));\n}","typeGuard":null,"tryCatchPattern":"try {\n  await doc.incrementalPatch(patch);\n} catch (err: any) {\n  if (err.code === 'DOC9') {\n    const field = err.parameters?.fieldName;\n    // strip the immutable field and retry\n    const { [field]: _omit, ...rest } = patch;\n    await doc.incrementalPatch(rest);\n  } else throw err;\n}","preventionTips":["Never include the primary key in patch/update payloads.","Strip finalFields from user-supplied update objects before applying them.","Only mark fields final when they truly must never change; changing them later requires a schema migration.","Build patches from explicit field lists, not from spreading whole state objects."],"tags":["schema","final-fields","immutability","update"],"backgroundTag":"final-field-modification","analyzedSha":"af6fb65f94cd70558d74c23799eda11ff1c15224","analyzedAt":"2026-08-31T23:32:37.842Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}