toeverything/AFFiNE · error · BlockSuiteError

ValueNotExists

ValueNotExists

Error message

EmbedIframeService or LinkPreviewService not found

What it means

parseIdentifier accepts only two shapes: an Identifier created by createIdentifier (a callable carrying identifierName/variant), or a named class/constructor from which a stable name+hash identifier is derived. Anything else — strings, undefined, numbers, plain objects, anonymous functions — cannot be mapped to a service key, so it throws 'Input is not a service identifier.' before any registry lookup happens. Unlike string-token DI libraries (inversify/tsyringe), raw strings are not valid tokens here.

Solutions

  1. Create identifiers with createIdentifier<T>('Name') and pass that callable to impl/get/getAll
  2. Fix the circular import so the identifier is defined when get()/impl() runs — move identifiers to a leaf module or reorder imports
  3. Never pass strings or plain objects as identifiers; if you meant class-as-identifier, pass the class itself (it must have a .name)

Example fix

// before
provider.get('Storage'); // strings are not identifiers

// after
const Storage = createIdentifier<Storage>('Storage');
fw.impl(Storage, LocalStorage);
provider.get(Storage);
Defensive patterns

Strategy: type-guard

Validate before calling

null

Type guard

function isIdentifier<T>(x: unknown): x is Identifier<T> {
  return typeof x === 'function' && typeof (x as any).identifierName === 'string';
}

// guard before resolving
if (!isIdentifier(Storage)) throw new Error('Storage identifier not initialized (circular import?)');
provider.get(Storage);

Prevention

When it happens

Trigger: provider.get('Storage') expecting a string token; passing undefined because a circular ESM import left the identifier module half-evaluated; passing a plain object or a value obtained by destructuring/wrapping the Identifier; passing an anonymous function expression (empty .name).

Common situations: Migrating code from a string-token DI container; circular imports where `export const Storage = createIdentifier(...)` is consumed before evaluation; identifiers re-exported through barrel files involved in a cycle.

Related errors


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

Appendix: source

Thrown at blocksuite/affine/blocks/embed/src/embed-iframe-block/embed-iframe-block.ts:163

  };

  refreshData = async () => {
    try {
      const { url } = this.model.props;
      if (!url) {
        this.status$.value = 'idle';
        return false;
      }

      // set loading status
      this.status$.value = 'loading';
      this.error$.value = null;

      // get embed data
      const embedIframeService = this.embedIframeService;
      const linkPreviewService = this.linkPreviewService;
      if (!embedIframeService || !linkPreviewService) {
        throw new BlockSuiteError(
          ErrorCode.ValueNotExists,
          'EmbedIframeService or LinkPreviewService not found'
        );
      }

      // get embed data and preview data in a promise
      const [embedData, previewData] = await Promise.all([
        embedIframeService.getEmbedIframeData(url),
        linkPreviewService.query(url),
      ]);

      // if the embed data is not found, and the iframeUrl is not set, throw an error
      const currentIframeUrl = this.model.props.iframeUrl;
      if (!embedData && !currentIframeUrl) {
        throw new BlockSuiteError(
          ErrorCode.ValueNotExists,
          'Failed to get embed data'
        );

View on GitHub (pinned to b4c8548c09)