{"record":{"id":"6eca6c4c7f2357b8","repo":"toeverything/AFFiNE","slug":"root-block-cannot-have-parent-parentflavour","errorCode":null,"errorMessage":"Root block cannot have parent: ${parentFlavour}.","messagePattern":"Root block cannot have parent: (.+?)\\.","errorType":"validation","errorClass":"SchemaValidateError","httpStatus":null,"severity":"error","filePath":"blocksuite/framework/store/src/schema/schema.ts","lineNumber":243,"sourceCode":"      });\n    });\n  }\n\n  /**\n   * Validates the role relationship between child and parent schemas.\n   * Throws if the child is a root block but has a parent.\n   *\n   * @param child - The child block schema.\n   * @param parent - The parent block schema.\n   * @throws {SchemaValidateError} If the child is a root block with a parent.\n   */\n  private _validateRole(child: BlockSchemaType, parent: BlockSchemaType) {\n    const childRole = child.model.role;\n    const childFlavour = child.model.flavour;\n    const parentFlavour = parent.model.flavour;\n\n    if (childRole === 'root') {\n      throw new SchemaValidateError(\n        childFlavour,\n        `Root block cannot have parent: ${parentFlavour}.`\n      );\n    }\n  }\n\n  /**\n   * Checks if the child flavour is valid under the parent flavour.\n   *\n   * @param child - The child block flavour name.\n   * @param parent - The parent block flavour name.\n   * @returns True if the relationship is valid, false otherwise.\n   */\n  isValid(child: string, parent: string) {\n    const childSchema = this.flavourSchemaMap.get(child);\n    const parentSchema = this.flavourSchemaMap.get(parent);\n    if (!childSchema || !parentSchema) {\n      return false;","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/blocksuite/framework/store/src/schema/schema.ts#L225-L261","documentation":"Thrown by the private _validateRole during validateSchema(child, parent): any child schema whose model.role === 'root' is rejected under any parent, with the offending parent flavour named in the message. You reach this path through validate()'s validateChildren (a root block listed among childFlavours) or through direct pairwise checks like schema.isValid / validateSchema.","triggerScenarios":"schema.validate(parent, undefined, ['affine:page']) — a root flavour listed as a child; validateSchema(rootSchema, parentSchema) invoked by hierarchy tooling; addBlock of a root-flavoured block under a parent via code paths that use validateSchema.","commonSituations":"Copy-paste or drag-drop logic that accepts any block as a child, including roots; schema children lists written by hand that accidentally include a root flavour.","solutions":["Remove root-role flavours from children lists and never pass them as children.","Guard with schema.get(flavour)?.model.role !== 'root' before treating a block as a child.","Use schema.isValid(childFlavour, parentFlavour) (non-throwing) to filter candidate children."],"exampleFix":"// before\ndoc.schema.validate('affine:note', undefined, ['affine:page']); // root as child\n\n// after\ndoc.schema.validate('affine:note', undefined, ['affine:paragraph']);","handlingStrategy":"validation","validationCode":"if (doc.schema.get(childFlavour)?.model.role === 'root') {\n  throw new Error(`${childFlavour} is a root block and cannot be a child`);\n}\ndoc.schema.validate(parentFlavour, undefined, [childFlavour]);","typeGuard":"const canBeChild = (schema: Schema, flavour: string): boolean =>\n  schema.get(flavour)?.model.role !== 'root';","tryCatchPattern":"if (!doc.schema.isValid(childFlavour, parentFlavour)) {\n  // skip or re-target: isValid wraps validateSchema without throwing\n}","preventionTips":["Filter candidate children by role before building hierarchies dynamically.","Never list root flavours in any schema's children array."],"tags":["schema","root-block","hierarchy","blocksuite"],"backgroundTag":"invalid-block-hierarchy","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}