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

  1. Override the static key on your subclass: `static override readonly key = 'my-extension';`.
  2. Use a unique, stable key (it becomes the DI identifier).
  3. 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

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.