toeverything/AFFiNE · error · BlockSuiteError
ErrorCode.GfxBlockElementError
ErrorCode.GfxBlockElementError
Error message
Error on rendering '${this.model.flavour}': Gfx block's model should have 'xywh' property. What it means
Thrown by `GfxBlockComponent.getRenderingRect` when `this.model.xywh$` is undefined. Gfx (surface/edgeless) blocks must declare a reactive `xywh` property in their model schema; the rendering rect (position + size + z-index) cannot be computed without it. Thrown during `renderBlock()` on every paint of the gfx block.
Source
Thrown at blocksuite/framework/std/src/view/element/gfx-block-component.ts:162
getCSSTransform() {
const viewport = this.gfx.viewport;
const { translateX, translateY, zoom, viewScale } = viewport;
const bound = Bound.deserialize(this.model.xywh);
const scaledX = (bound.x * zoom) / viewScale;
const scaledY = (bound.y * zoom) / viewScale;
const deltaX = scaledX - bound.x;
const deltaY = scaledY - bound.y;
return `translate(${translateX / viewScale + deltaX}px, ${translateY / viewScale + deltaY}px) scale(${this.getCSSScaleVal()})`;
}
getRenderingRect() {
const { xywh$ } = this.model;
if (!xywh$) {
throw new BlockSuiteError(
ErrorCode.GfxBlockElementError,
`Error on rendering '${this.model.flavour}': Gfx block's model should have 'xywh' property.`
);
}
const [x, y, w, h] = JSON.parse(xywh$.value);
return { x, y, w, h, zIndex: this.toZIndex() };
}
override renderBlock() {
const { x, y, w, h, zIndex } = this.getRenderingRect();
if (this.style.left !== `${x}px`) this.style.left = `${x}px`;
if (this.style.top !== `${y}px`) this.style.top = `${y}px`;
if (this.style.width !== `${w}px`) this.style.width = `${w}px`;
if (this.style.height !== `${h}px`) this.style.height = `${h}px`;
if (this.style.zIndex !== zIndex) this.style.zIndex = zIndex;View on GitHub (pinned to 26c515e050)
Solutions
- Add `xywh` to the block's schema props: `xywh: f.rect(0, 0, 100, 100)` (or the project's rect serializer).
- Ensure the model extends `GfxBlockElementModel` and is registered with the surface schema.
- Run a migration that backfills `xywh` on existing blocks of this flavour.
Example fix
// before: schema missing xywh
defineBlockSchema({
flavour: 'my:gfx',
toModel: () => new GfxBlockElementModel(),
props: () => ({ color: f.string('red') }),
})
// after: declare xywh so getRenderingRect can read it
defineBlockSchema({
flavour: 'my:gfx',
toModel: () => new GfxBlockElementModel(),
props: () => ({
color: f.string('red'),
xywh: f.rect(0, 0, 100, 100),
}),
}) Defensive patterns
Strategy: validation
Validate before calling
// verify the model declares xywh before rendering the gfx block
function hasXywh(model: BlockModel): boolean {
return !!(model as any).xywh$;
}
if (hasXywh(model)) render(/* gfx block */);
else console.warn('Cannot render gfx block without xywh'); Type guard
import type { GfxBlockElementModel } from '@blocksuite/std/gfx';
function isGfxModelWithXywh(m: BlockModel): m is GfxBlockElementModel {
return 'xywh$' in m && !!(m as GfxBlockElementModel).xywh$;
} Try / catch
try {
block.getRenderingRect();
} catch (e) {
if (e instanceof BlockSuiteError && e.code === ErrorCode.GfxBlockElementError) {
console.error('Gfx block missing xywh:', e.message);
}
} Prevention
- Always declare xywh in the gfx block's schema props.
- Extend GfxBlockElementModel for gfx blocks.
- Ship migrations to backfill xywh on legacy blocks.
When it happens
Trigger: A block model is registered/toasted as a gfx block (`GfxBlockElementModel`) but its schema's `props()` does not define `xywh: f.flatserialize(...)`, or a plain `BlockModel` was used where a `GfxBlockElementModel` was expected.
Common situations: Converting a doc-mode block to a gfx block without adding `xywh` to the schema; custom block flavour whose define method omits the geometry prop; deserializing a snapshot whose model lacked `xywh`.
Related errors
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/7dec54f7d5fdd99a.
Report an issue: GitHub.