toeverything/AFFiNE · error · BlockSuiteError
ErrorCode.ValueNotExists
ErrorCode.ValueNotExists
Error message
Key is not defined in the StoreExtension
What it means
Thrown by `StoreExtension.setup` when a subclass of `StoreExtension` is being registered but its static `key` property is undefined. The base class declares `static readonly key: string` with no initializer; subclasses MUST override it. The DI container uses this key to expose the extension under `StoreExtensionIdentifier(key)`.
Source
Thrown at blocksuite/framework/store/src/extension/store-extension.ts:44
constructor(readonly store: Store) {
super();
}
/**
* Lifecycle hook when the yjs document is loaded.
*/
loaded() {}
/**
* Lifecycle hook when the yjs document is disposed.
*/
disposed() {}
static readonly [storeExtensionSymbol] = true;
static override setup(di: Container) {
if (!this.key) {
throw new BlockSuiteError(
ErrorCode.ValueNotExists,
'Key is not defined in the StoreExtension'
);
}
di.add(this, [StoreIdentifier]);
di.addImpl(StoreExtensionIdentifier(this.key), provider =>
provider.get(this)
);
}
}
export function isStoreExtensionConstructor(
extension: object
): extension is typeof StoreExtension {
return storeExtensionSymbol in extension;
}
View on GitHub (pinned to 26c515e050)
Solutions
- Override the static key on your subclass: `static override readonly key = 'my-extension';`.
- Use a unique, stable key (it becomes the DI identifier).
- If you do not need store lifecycle hooks, extend `Extension` instead of `StoreExtension`.
Example fix
// before: missing key override
class MyExt extends StoreExtension {
override loaded() { /* ... */ }
}
// after: declare a unique static key
class MyExt extends StoreExtension {
static override readonly key = 'my-ext';
override loaded() { /* ... */ }
} Defensive patterns
Strategy: validation
Validate before calling
// verify the subclass declares its key before registering
function extensionHasKey(ext: typeof StoreExtension): boolean {
return typeof (ext as any).key === 'string' && (ext as any).key.length > 0;
}
if (extensionHasKey(MyExt)) storeExtensions.push(MyExt);
else throw new Error('StoreExtension subclass must override static key'); Type guard
function isKeyedStoreExtension(ext: typeof StoreExtension): ext is typeof StoreExtension & { key: string } {
return typeof (ext as any).key === 'string';
} Try / catch
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
try {
MyExt.setup(container);
} catch (e) {
if (e instanceof BlockSuiteError && e.code === ErrorCode.ValueNotExists) {
console.error('StoreExtension missing static key:', MyExt.name);
}
} Prevention
- Always override `static readonly key` on StoreExtension subclasses.
- Use a unique, stable key value.
- Extend plain Extension if you do not need store lifecycle hooks.
When it happens
Trigger: Extending `StoreExtension` (e.g. to build a custom store-level extension) and passing the subclass to the store's extensions list without setting `static override readonly key = 'something'`. The static `setup` runs at store construction and trips the guard.
Common situations: Copy-pasting the `StoreExtension` boilerplate and forgetting the key; renaming the key to `undefined`; using a plain `Extension` instead of `StoreExtension` when you actually want store lifecycle hooks.
Related errors
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/3c5a002c6c4eacc2.
Report an issue: GitHub.