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

  1. Add `xywh` to the block's schema props: `xywh: f.rect(0, 0, 100, 100)` (or the project's rect serializer).
  2. Ensure the model extends `GfxBlockElementModel` and is registered with the surface schema.
  3. 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

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.