toeverything/AFFiNE · error · BlockSuiteError

DatabaseBlockError

DatabaseBlockError

Error message

Unknown view type: ${type}

What it means

Framework.addFactory keys registrations by (scope stack, identifierName, variant). Registering a second factory for the exact same combination without the override flag throws DuplicateDefinitionError — the container treats double registration as a programming error rather than silently replacing the first implementation, which would hide order-dependent bugs.

Solutions

  1. If the second registration should win, use framework.override(Identifier, Impl) — it replaces the existing factory (override(Identifier, null) removes it)
  2. If both implementations are needed simultaneously, register them as variants: framework.impl(Storage('local'), LocalStorage) and framework.impl(Storage('session'), SessionStorage)
  3. If the duplicate is accidental, delete the second registration or guard setup so it runs once per Framework instance (module-level flag, or build a fresh Framework per test)

Example fix

// before
fw.impl(Storage, LocalStorage);
fw.impl(Storage, LocalStorage); // DuplicateDefinitionError: [Storage] already exists

// after
fw.impl(Storage, LocalStorage);
fw.override(Storage, SessionStorage); // intentionally replaces
// or use variants:
fw.impl(Storage('session'), SessionStorage);
Defensive patterns

Strategy: try-catch

Validate before calling

const registered = new Set<string>();
function registerOnce(fw: Framework, id: Identifier<any>, impl: any, key: string) {
  if (registered.has(key)) {
    fw.override(id, impl); // second pass replaces instead of throwing
  } else {
    fw.impl(id, impl);
    registered.add(key);
  }
}

Type guard

null

Try / catch

try {
  fw.impl(Storage, LocalStorage);
} catch (err) {
  if (err instanceof DuplicateDefinitionError) {
    fw.override(Storage, LocalStorage); // intentional replacement
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling framework.impl(Storage, LocalStorage) twice with the default variant; two modules both registering the same interface and both being imported; a plugin/test setup function that calls impl() running again on the same Framework (HMR reload, repeated beforeEach) without cleanup; registering the same identifier under the same scope stack via framework.scope(ScopeA).impl(...).

Common situations: Hot module reload re-executing registration modules; test setup helpers that add mocks and are invoked per test against a shared Framework; copy-pasted registrations; merging two modules that each register a shared service.

Related errors


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

Appendix: source

Thrown at blocksuite/affine/blocks/database/src/data-source.ts:606

  viewDataGet(viewId: string): DataViewDataType | undefined {
    return this.viewDataList$.value.find(data => data.id === viewId)!;
  }

  viewDataMoveTo(id: string, position: InsertToPosition): void {
    moveViewTo(this._model, id, position);
  }

  viewDataUpdate<ViewData extends DataViewDataType>(
    id: string,
    updater: (data: ViewData) => Partial<ViewData>
  ): void {
    updateView(this._model, id, updater);
  }

  viewMetaGet(type: string): ViewMeta {
    const view = databaseBlockViewMap[type];
    if (!view) {
      throw new BlockSuiteError(
        ErrorCode.DatabaseBlockError,
        `Unknown view type: ${type}`
      );
    }
    return view;
  }

  viewMetaGetById(viewId: string): ViewMeta | undefined {
    const view = this.viewDataGet(viewId);
    if (!view) {
      return;
    }
    return this.viewMetaGet(view.mode);
  }
}

export const databaseViewInitTemplate = (
  datasource: DatabaseBlockDataSource,

View on GitHub (pinned to b4c8548c09)