{"record":{"id":"2835ee65fb29a897","repo":"toeverything/AFFiNE","slug":"reactiveproxyerror-2835ee","errorCode":"ReactiveProxyError","errorMessage":"key cannot be a symbol","messagePattern":"key cannot be a symbol","errorType":"exception","errorClass":"BlockSuiteError","httpStatus":null,"severity":"error","filePath":"blocksuite/framework/store/src/reactive/proxy.ts","lineNumber":52,"sourceCode":"\n          this._updateWithSkip(() => {\n            this._source.splice(retain, 0, ...proxyList);\n          });\n\n          retain += change.insert.length;\n        }\n      });\n    });\n  };\n\n  protected _getProxy = () => {\n    return new Proxy(this._source, {\n      has: (target, p) => {\n        return Reflect.has(target, p);\n      },\n      set: (target, p, value, receiver) => {\n        if (typeof p !== 'string') {\n          throw new BlockSuiteError(\n            ErrorCode.ReactiveProxyError,\n            'key cannot be a symbol'\n          );\n        }\n\n        const index = Number(p);\n        if (this._skipNext || Number.isNaN(index)) {\n          return Reflect.set(target, p, value, receiver);\n        }\n\n        if (this._stashed.has(index)) {\n          const result = Reflect.set(target, p, value, receiver);\n          this._options.onChange?.(this._proxy, true);\n          return result;\n        }\n\n        const reactive = proxies.get(this._ySource);\n        if (!reactive) {","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/blocksuite/framework/store/src/reactive/proxy.ts#L34-L70","documentation":"ReactiveYArray wraps a plain JS array and forwards writes into a Yjs Y.Array inside a doc transaction. Yjs keys are string-addressable only, so the proxy's `set` trap hard-rejects any write whose property key is a symbol before any data is touched. This is an invariant guard, not a data error: nothing is partially written when it fires.","triggerScenarios":"`arrProxy[Symbol('meta')] = value`; `Object.assign(reactiveArray, objWithSymbolKeys)`; a dependency (Vue reactivity flags, immer, class instances carrying symbol internals) stamping symbol-keyed metadata onto the reactive array; overriding `arrProxy[Symbol.iterator]`.","commonSituations":"Passing a BlockSuite reactive array into a reactivity/serialization library that decorates objects with symbol keys; using symbol keys as collision-free metadata on shared state; HMR setups that reuse proxies with framework internals that expect symbols.","solutions":["Store symbol-keyed metadata in a side WeakMap keyed by the proxy instead of on the proxy itself","Use plain string keys for any property you intentionally attach to the array","Hand third-party code a plain copy (`[...arrProxy]` / `structuredClone(toJSON(arrProxy))`) so it stamps symbols on the copy, not the reactive proxy","If the writer cannot be controlled, catch BlockSuiteError with code ReactiveProxyError and fall back to non-reactive storage"],"exampleFix":"// before\nyArrayProxy[Symbol('meta')] = { touchedAt: Date.now() };\n\n// after\nconst meta = new WeakKeyMap... // use a side table\nconst meta = new WeakMap<object, unknown>();\nmeta.set(yArrayProxy, { touchedAt: Date.now() });","handlingStrategy":"type-guard","validationCode":"if (typeof key !== 'string') {\n  sideTable.set(arrProxy, value); // never write symbol keys to the proxy\n} else {\n  arrProxy[key] = value;\n}","typeGuard":"const isStringKey = (key: PropertyKey): key is string => typeof key === 'string';","tryCatchPattern":"try {\n  arrProxy[key] = value;\n} catch (e) {\n  if (e instanceof BlockSuiteError && e.code === ErrorCode.ReactiveProxyError && e.message.includes('symbol')) {\n    sideTable.set(arrProxy, value); // graceful fallback\n  } else throw e;\n}","preventionTips":["Never attach symbol-keyed properties to reactive arrays; use a side WeakMap for metadata","Pass plain copies to third-party libraries that decorate objects with symbols","Iterate Object.keys(), not Reflect.ownKeys(), when writing or deleting dynamically"],"tags":["reactive-proxy","symbol","yjs","blocksuite"],"backgroundTag":"proxy-symbol-key-unsupported","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}