toeverything/AFFiNE · error · SchemaValidateError
Block cannot have parent: ${parent.model.flavour}.
Error message
Block cannot have parent: ${parent.model.flavour}. What it means
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.
Source
Thrown at blocksuite/framework/store/src/schema/schema.ts:319
)
);
}
/**
* Validates the relationship between a child and parent schema.
* Throws if the relationship is invalid.
*
* @param child - The child block schema.
* @param parent - The parent block schema.
* @throws {SchemaValidateError} If the relationship is invalid.
*/
validateSchema(child: BlockSchemaType, parent: BlockSchemaType) {
this._validateRole(child, parent);
const relationCheckSuccess = this._validateParent(child, parent);
if (!relationCheckSuccess) {
throw new SchemaValidateError(
child.model.flavour,
`Block cannot have parent: ${parent.model.flavour}.`
);
}
}
}
View on GitHub (pinned to b4c8548c09)
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.
Example fix
// before
// container schema: { flavour: 'my:box', role: 'hub', children: ['affine:note'] }
await doc.addBlock('affine:paragraph', {}, boxId); // rejected
// after
// container schema: { flavour: 'my:box', role: 'hub', children: ['affine:note', 'affine:paragraph'] }
await doc.addBlock('affine:paragraph', {}, boxId); Defensive patterns
Strategy: validation
Validate before calling
if (!doc.schema.isValid(childFlavour, parentFlavour)) {
throw new Error(`${childFlavour} is not allowed under ${parentFlavour}`);
}
await doc.addBlock(childFlavour, props, parentId); Type guard
const isAllowedChild = (schema: Schema, child: string, parent: string): boolean => schema.isValid(child, parent);
Try / catch
try {
await doc.addBlock(childFlavour, props, parentId);
} catch (e) {
if (e instanceof SchemaValidateError && e.message.includes('Block cannot have parent')) {
// pick a compatible parent from schema.get(parentFlavour).model.children
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ErrorCode.ModelCRUDError
- ErrorCode.ModelCRUDError
- ErrorCode.ModelCRUDError
- Schema not found. The block flavour may not be registered.
- Root block cannot have parent.
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/cff87b6ca8ebace0.
Report an issue: GitHub.