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

GfxBlockElement.getRenderingRect() (gfx-block-component.ts) computes the element's geometry from the model's reactive xywh$ signal; a gfx (surface/edgeless) block without an 'xywh' string prop cannot be positioned, so rendering aborts with GfxBlockElementError. The flavour named in the message is the block whose schema or instance lacks xywh.

Solutions

  1. Add xywh to the flavour's props schema (props: { xywh: z.string().optional()... } per BlockSchemaDefinition) and always pass a '[x,y,w,h]' JSON string on creation.
  2. When inserting into edgeless, use the surface/gfx insert APIs that compute xywh for you.
  3. Repair existing docs by backfilling a default xywh for models missing it before render.
  4. If the block is not meant for edgeless, render it in doc mode / don't mount it under the gfx container.

Example fix

// before
const MyBlockSchema = defineBlockSchema({
  flavour: 'my:block',
  props: => ({ text: z.string() }), // no xywh
});
doc.addBlock('my:block', {}, surfaceId);

// after
props: => ({ text: z.string(), xywh: z.string() }),
doc.addBlock('my:block', { xywh: '[0,0,200,80]' }, surfaceId);
Defensive patterns

Strategy: validation

Validate before calling

const model = doc.getModelById(id);
if (model && typeof model.xywh === 'string') {
  // safe to render in edgeless/gfx context
} else {
  doc.addBlock? doc.updateBlock(model, { xywh: '[0,0,200,80]' }); // backfill before render
}

Type guard

function hasXywh(model: { xywh?: unknown }): boolean {
  return typeof model.xywh === 'string' && model.xywh.length > 0;
}

Prevention

When it happens

Trigger: Rendering a block flavour in the edgeless/gfx context whose schema does not define an xywh: z.string() prop, or whose model instance never received an xywh value (created via doc.addBlock without xywh, or imported from a snapshot missing it).

Common situations: Writing a custom edgeless block and forgetting the xywh prop in the FlavourDefine/schema; creating blocks with doc.addBlock({flavour:'affine:embed-...'}) without xywh; importing third-party snapshots that omit xywh; reusing a doc block flavour on the surface without adaptation.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/7dec54f7d5fdd99a. Report an issue: GitHub.

Appendix: 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 b4c8548c09)