{"record":{"id":"cff87b6ca8ebace0","repo":"toeverything/AFFiNE","slug":"block-cannot-have-parent-parent-model-flavour","errorCode":null,"errorMessage":"Block cannot have parent: ${parent.model.flavour}.","messagePattern":"Block cannot have parent: (.+?)\\.","errorType":"validation","errorClass":"SchemaValidateError","httpStatus":null,"severity":"error","filePath":"blocksuite/framework/store/src/schema/schema.ts","lineNumber":319,"sourceCode":"      )\n    );\n  }\n\n  /**\n   * Validates the relationship between a child and parent schema.\n   * Throws if the relationship is invalid.\n   *\n   * @param child - The child block schema.\n   * @param parent - The parent block schema.\n   * @throws {SchemaValidateError} If the relationship is invalid.\n   */\n  validateSchema(child: BlockSchemaType, parent: BlockSchemaType) {\n    this._validateRole(child, parent);\n\n    const relationCheckSuccess = this._validateParent(child, parent);\n\n    if (!relationCheckSuccess) {\n      throw new SchemaValidateError(\n        child.model.flavour,\n        `Block cannot have parent: ${parent.model.flavour}.`\n      );\n    }\n  }\n}\n","sourceCodeStart":301,"sourceCodeEnd":326,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/blocksuite/framework/store/src/schema/schema.ts#L301-L326","documentation":"validateSchema calls _validateParent(child, parent), which matches the child against the parent's model.children allow-list (default ['*']) and the parent against the child's model.parent list (default ['*']), including minimatch wildcards and '@role' syntax. If no combination matches, the pairing is rejected with 'Block cannot have parent: ...'. This is the general 'this child is not allowed under that parent' error.","triggerScenarios":"Parent schema declares children: ['affine:note'] but you addBlock an affine:paragraph under it; child declares parent: ['affine:note'] but is added under a different container; role tokens ('@hub') that do not match the actual roles.","commonSituations":"Custom container blocks with restrictive children lists; refactors that move blocks under new containers without updating parent/children constraints; role-based schemas where a block's role changed.","solutions":["Add the child flavour (or '*' / a matching '@role') to the parent schema's model.children list.","Add the parent flavour to the child schema's model.parent list.","Choose a compatible parent container for the child you are inserting.","Pre-check with doc.schema.isValid(childFlavour, parentFlavour), which wraps validateSchema without throwing."],"exampleFix":"// before\n// container schema: { flavour: 'my:box', role: 'hub', children: ['affine:note'] }\nawait doc.addBlock('affine:paragraph', {}, boxId); // rejected\n\n// after\n// container schema: { flavour: 'my:box', role: 'hub', children: ['affine:note', 'affine:paragraph'] }\nawait doc.addBlock('affine:paragraph', {}, boxId);","handlingStrategy":"validation","validationCode":"if (!doc.schema.isValid(childFlavour, parentFlavour)) {\n  throw new Error(`${childFlavour} is not allowed under ${parentFlavour}`);\n}\nawait doc.addBlock(childFlavour, props, parentId);","typeGuard":"const isAllowedChild = (schema: Schema, child: string, parent: string): boolean =>\n  schema.isValid(child, parent);","tryCatchPattern":"try {\n  await doc.addBlock(childFlavour, props, parentId);\n} catch (e) {\n  if (e instanceof SchemaValidateError && e.message.includes('Block cannot have parent')) {\n    // pick a compatible parent from schema.get(parentFlavour).model.children\n  } else throw e;\n}","preventionTips":["Pre-check every insertion with schema.isValid(childFlavour, parentFlavour).","Keep parent/children allow-lists in custom schemas explicit and test them with validateSchema in unit tests.","When refactoring containers, update both the child's parent list and the parent's children list."],"tags":["schema","hierarchy","parent-child","flavour","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"}