toeverything/AFFiNE · error · SchemaValidateError
None root block must have parent.
Error message
None root block must have parent.
What it means
The counterpart of the root rule: Schema.validate throws when a block whose model.role is not 'root' is validated without a parentFlavour. Every non-root block must live under a parent, so validating or adding one standalone is an invalid hierarchy.
Source
Thrown at blocksuite/framework/store/src/schema/schema.ts:91
}
this.validateSchema(childSchema, schema);
});
};
if (schema.model.role === 'root') {
if (parentFlavour) {
throw new SchemaValidateError(
schema.model.flavour,
'Root block cannot have parent.'
);
}
validateChildren();
return;
}
if (!parentFlavour) {
throw new SchemaValidateError(
schema.model.flavour,
'None root block must have parent.'
);
}
const parentSchema = this.flavourSchemaMap.get(parentFlavour);
if (!parentSchema) {
throw new SchemaValidateError(parentFlavour, SCHEMA_NOT_FOUND_MESSAGE);
}
this.validateSchema(schema, parentSchema);
validateChildren();
};
/**
* Returns an object mapping each registered flavour to its version number.
*/
get versions() {
return Object.fromEntries(View on GitHub (pinned to b4c8548c09)
Solutions
- Pass the parent block id: doc.addBlock('affine:paragraph', {}, noteModel.id).
- Ensure the doc has a root and an intermediate container before adding children (usually via doc.load()).
- If the block is intentionally top-level, its schema must declare model.role = 'root'.
Example fix
// before
await doc.addBlock('affine:paragraph'); // no parent
// after
const noteId = doc.root?.children[0]?.id;
await doc.addBlock('affine:paragraph', {}, noteId); Defensive patterns
Strategy: validation
Validate before calling
const role = doc.schema.get(flavour)?.model.role;
if (role && role !== 'root' && !parentId) {
throw new Error(`flavour ${flavour} requires a parent block id`);
}
await doc.addBlock(flavour, props, parentId); Type guard
const needsParent = (schema: Schema, flavour: string): boolean => schema.get(flavour)?.model.role !== 'root';
Try / catch
try {
await doc.addBlock(flavour);
} catch (e) {
if (e instanceof SchemaValidateError && e.message.includes('must have parent')) {
await doc.addBlock(flavour, props, resolvedParentId);
} else throw e;
} Prevention
- Ensure the doc has a root and container hierarchy (doc.load()) before adding children.
- Make parent id a required parameter in your own wrappers around addBlock for non-root flavours.
When it happens
Trigger: doc.addBlock('affine:paragraph') with no parent id passed; validating a child flavour with schema.validate(flavour) and no second argument; snapshot import where the parent block was dropped.
Common situations: Forgetting the parent argument in addBlock during prototyping; docs loaded from partial snapshots that lost the root; constructing test hierarchies by hand.
Related errors
- Root block cannot have parent.
- Root block cannot have parent: ${parentFlavour}.
- Block cannot have parent: ${parent.model.flavour}.
- ErrorCode.ModelCRUDError
- ErrorCode.ModelCRUDError
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/3bdc7953bb01d858.
Report an issue: GitHub.