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
- If the second registration should win, use framework.override(Identifier, Impl) — it replaces the existing factory (override(Identifier, null) removes it)
- If both implementations are needed simultaneously, register them as variants: framework.impl(Storage('local'), LocalStorage) and framework.impl(Storage('session'), SessionStorage)
- 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
- Make registration modules idempotent: guard with a module-level Set or run setup once per Framework instance
- Use framework.override() whenever a later registration is meant to win
- Use variants (Identifier('name')) for coexisting implementations instead of double-registering the default variant
- Build a fresh Framework per test to avoid cross-test duplicate registrations
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)