{"record":{"id":"3a9fe509b1f82162","repo":"toeverything/AFFiNE","slug":"errorcode-selectionerror","errorCode":"ErrorCode.SelectionError","errorMessage":"You must override this method","messagePattern":"You must override this method","errorType":"exception","errorClass":"BlockSuiteError","httpStatus":null,"severity":"error","filePath":"blocksuite/framework/store/src/extension/selection/base.ts","lineNumber":31,"sourceCode":"\n  static readonly recoverable: boolean = false;\n\n  readonly blockId: string;\n\n  get group(): string {\n    return (this.constructor as SelectionConstructor).group;\n  }\n\n  get type(): string {\n    return (this.constructor as SelectionConstructor).type as string;\n  }\n\n  constructor({ blockId }: BaseSelectionOptions) {\n    this.blockId = blockId;\n  }\n\n  static fromJSON(_: Record<string, unknown>): BaseSelection {\n    throw new BlockSuiteError(\n      ErrorCode.SelectionError,\n      'You must override this method'\n    );\n  }\n\n  abstract equals(other: BaseSelection): boolean;\n\n  is<T extends SelectionConstructor>(\n    type: T\n  ): this is T extends SelectionConstructor<infer U> ? U : never {\n    return this.type === type.type;\n  }\n\n  abstract toJSON(): Record<string, unknown>;\n}\n","sourceCodeStart":13,"sourceCodeEnd":47,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/blocksuite/framework/store/src/extension/selection/base.ts#L13-L47","documentation":"BaseSelection.fromJSON() is the base stub that every concrete selection class must override, because each selection type needs its own deserialization logic. If a registered selection subclass ships without a static fromJSON override, the first deserialization (undo/redo restoring selection state, remote selection updates, persisted selection) dispatches to the stub and throws SelectionError 'You must override this method'.","triggerScenarios":"Defining a custom selection class extending BaseSelection without implementing static fromJSON(json), then triggering selection (de)serialization: undoManager pop restoring 'selection-state' from stack item meta, or selectionManager.set / fromJSON on remote updates.","commonSituations":"Writing custom block selection types for new flavours; copying a selection class skeleton from docs/examples that omits fromJSON; minor-version upgrades where fromJSON became required/typed differently.","solutions":["Implement static fromJSON(json) on the selection subclass: parse the fields, return new MySelection({ blockId, ... }).","Mirror the fields you serialize in equals/onChange so round-trips are lossless.","Register the class via SelectionExtension(MySelection) so deserialization uses your constructor.","If you never persist/remote-sync this selection type, reconsider whether a custom class is needed at all."],"exampleFix":"// before\nclass MySelection extends BaseSelection {\n  static override type = 'my';\n  // no fromJSON -> throws when deserialized\n}\n\n// after\nclass MySelection extends BaseSelection {\n  static override type = 'my-selection';\n  static override fromJSON(json: Record<string, unknown>) {\n    return new MySelection({ blockId: json.blockId as string });\n  }\n  constructor(opts: BaseSelectionOptions) { super({ blockId: opts.blockId }); }\n  override equals(other: BaseSelection) { return other instanceof MySelection && other.blockId === this.blockId; }\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"import { BaseSelection } from '@blocksuite/store';\nfunction implementsFromJSON(\n  ctor: typeof BaseSelection\n): boolean {\n  return ctor.fromJSON !== BaseSelection.fromJSON;\n}\n// guard before registration:\nif (!implementsFromJSON(MySelection)) throw new Error('MySelection must implement static fromJSON');","tryCatchPattern":null,"preventionTips":["Implement static fromJSON on every selection subclass before registering it.","Add a startup assertion that each registered selection overrides fromJSON.","Cover serialization round-trips (set → undo → restore) in tests for custom selection types."],"tags":["blocksuite","selection","serialization","abstract-method","custom-extension"],"backgroundTag":"abstract-method-not-implemented","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"}