toeverything/AFFiNE · error · SchemaValidateError
Root block cannot have parent: ${parentFlavour}.
Error message
Root block cannot have parent: ${parentFlavour}. What it means
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.
Source
Thrown at blocksuite/framework/store/src/schema/schema.ts:243
});
});
}
/**
* Validates the role relationship between child and parent schemas.
* Throws if the child is a root block but has a parent.
*
* @param child - The child block schema.
* @param parent - The parent block schema.
* @throws {SchemaValidateError} If the child is a root block with a parent.
*/
private _validateRole(child: BlockSchemaType, parent: BlockSchemaType) {
const childRole = child.model.role;
const childFlavour = child.model.flavour;
const parentFlavour = parent.model.flavour;
if (childRole === 'root') {
throw new SchemaValidateError(
childFlavour,
`Root block cannot have parent: ${parentFlavour}.`
);
}
}
/**
* Checks if the child flavour is valid under the parent flavour.
*
* @param child - The child block flavour name.
* @param parent - The parent block flavour name.
* @returns True if the relationship is valid, false otherwise.
*/
isValid(child: string, parent: string) {
const childSchema = this.flavourSchemaMap.get(child);
const parentSchema = this.flavourSchemaMap.get(parent);
if (!childSchema || !parentSchema) {
return false;View on GitHub (pinned to b4c8548c09)
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.
Example fix
// before
doc.schema.validate('affine:note', undefined, ['affine:page']); // root as child
// after
doc.schema.validate('affine:note', undefined, ['affine:paragraph']); Defensive patterns
Strategy: validation
Validate before calling
if (doc.schema.get(childFlavour)?.model.role === 'root') {
throw new Error(`${childFlavour} is a root block and cannot be a child`);
}
doc.schema.validate(parentFlavour, undefined, [childFlavour]); Type guard
const canBeChild = (schema: Schema, flavour: string): boolean => schema.get(flavour)?.model.role !== 'root';
Try / catch
if (!doc.schema.isValid(childFlavour, parentFlavour)) {
// skip or re-target: isValid wraps validateSchema without throwing
} Prevention
- Filter candidate children by role before building hierarchies dynamically.
- Never list root flavours in any schema's children array.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Root block cannot have parent.
- None root block must have parent.
- Block cannot have parent: ${parent.model.flavour}.
- ErrorCode.NoRootModelError
- ErrorCode.ModelCRUDError
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/6eca6c4c7f2357b8.
Report an issue: GitHub.