{"record":{"id":"e093b460ad6084b6","repo":"toeverything/AFFiNE","slug":"reactiveproxyerror-e093b4","errorCode":"ReactiveProxyError","errorMessage":"Failed to transact text! yText is not attached to a doc","messagePattern":"Failed to transact text! yText is not attached to a doc","errorType":"exception","errorClass":"BlockSuiteError","httpStatus":null,"severity":"error","filePath":"blocksuite/framework/store/src/reactive/text/text.ts","lineNumber":99,"sourceCode":"    this._deltas$ = signal(this._yText.doc ? this._yText.toDelta() : []);\n    this._yText.observe(event => {\n      const isLocal =\n        !event.transaction.origin ||\n        !this._yText.doc ||\n        event.transaction.origin instanceof Y.UndoManager ||\n        event.transaction.origin.proxy\n          ? true\n          : event.transaction.origin === this._yText.doc.clientID;\n      this._length$.value = this._yText.length;\n      this._deltas$.value = this._yText.toDelta();\n      this._onChange?.(this._yText, isLocal);\n    });\n  }\n\n  private _transact(callback: () => void) {\n    const doc = this._yText.doc;\n    if (!doc) {\n      throw new BlockSuiteError(\n        ErrorCode.ReactiveProxyError,\n        'Failed to transact text! yText is not attached to a doc'\n      );\n    }\n    doc.transact(() => {\n      callback();\n    }, doc.clientID);\n  }\n\n  /**\n   * Apply a delta to the text.\n   *\n   * @param delta - The delta to apply.\n   *\n   * @example\n   * ```ts\n   * const text = new Text('Hello, world!');\n   * text.applyDelta([{insert: ' blocksuite', attributes: { bold: true }}]);","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/blocksuite/framework/store/src/reactive/text/text.ts#L81-L117","documentation":"Thrown by Text._transact (text.ts:96) when the wrapped Y.Text has no doc. Every mutating Text method (clear, delete, format, insert, replace, applyDelta, join, split) routes through _transact, which needs a Y.Doc to run doc.transact(callback, clientID). Without a doc the operation cannot be persisted or synced, so it throws.","triggerScenarios":"Calling any mutating Text method on a Text constructed from a detached Y.Text (new Text(new Y.Text('x')) where the Y.Text was never inserted into a doc). Also reached when a Text built from a plain string/delta is mutated before being attached, because string/delta constructors create a detached Y.Text (text.ts:60,73).","commonSituations":"Editor code that mutates block text before the block is mounted into the doc tree; tests using new Text('hello').delete(...) with no Y.Doc; cloning/detaching text for transform and then editing the clone; SSR rendering paths that call format/insert.","solutions":["Ensure the Text's Y.Text is part of a Y.Doc before mutating (e.g. insert it into a Y.Map owned by a doc, or obtain the Text from an attached block model).","For ad-hoc manipulation, wrap: const doc = new Y.Doc(); const yt = doc.getText('t'); yt.insert(0, 'hi'); const t = new Text(yt); t.insert(...).","Defer text edits until after the host block/collection reports the doc is ready."],"exampleFix":"// before\nconst t = new Text('hello');\nt.delete(0, 1); // throws: yText not attached\n\n// after\nconst doc = new Y.Doc();\nconst yt = doc.getText('t');\nyt.insert(0, 'hello');\nconst t = new Text(yt);\nt.delete(0, 1); // ok","handlingStrategy":"validation","validationCode":"import * as Y from 'yjs';\nimport { Text } from '@blocksuite/framework/store';\n\nfunction assertTextAttached(text: Text) {\n  if (!text.yText.doc) {\n    throw new Error('Text.yText is not attached to a Y.Doc');\n  }\n}\n\n// usage: build an attached Text\nconst doc = new Y.Doc();\nconst yt = doc.getText('body');\nyt.insert(0, 'hello');\nconst text = new Text(yt);\nassertTextAttached(text);\ntext.insert(' world', 5);","typeGuard":"function isTextAttached(text: Text): boolean {\n  return text.yText.doc != null;\n}","tryCatchPattern":null,"preventionTips":["Mutate block text only after the block is mounted into the doc tree.","In tests, always construct a Y.Doc and an attached Y.Text before wrapping in Text.","Defer edits until the host collection reports the doc is ready."],"tags":["yjs","text","reactive-proxy","lifecycle","doc-binding"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}