{"record":{"id":"839f76740884faba","repo":"toeverything/AFFiNE","slug":"nosurfacemodelerror","errorCode":"NoSurfaceModelError","errorMessage":"This doc is missing surface block in edgeless.","messagePattern":"This doc is missing surface block in edgeless\\.","errorType":"exception","errorClass":"BlockSuiteError","httpStatus":null,"severity":"error","filePath":"blocksuite/affine/blocks/root/src/edgeless/edgeless-root-service.ts","lineNumber":119,"sourceCode":"  }\n\n  get viewport() {\n    return this.std.get(GfxControllerIdentifier).viewport;\n  }\n\n  get zoom() {\n    return this.viewport.zoom;\n  }\n\n  get crud() {\n    return this.std.get(EdgelessCRUDIdentifier);\n  }\n\n  constructor(std: BlockStdScope, flavourProvider: { flavour: string }) {\n    super(std, flavourProvider);\n    const surface = getSurfaceBlock(this.doc);\n    if (!surface) {\n      throw new BlockSuiteError(\n        ErrorCode.NoSurfaceModelError,\n        'This doc is missing surface block in edgeless.'\n      );\n    }\n    this._surface = surface;\n  }\n\n  private _initReadonlyListener() {\n    const doc = this.doc;\n\n    const slots = this.std.get(EdgelessLegacySlotIdentifier);\n\n    let readonly = doc.readonly;\n    this.disposables.add(\n      effect(() => {\n        if (readonly !== doc.readonly) {\n          readonly = doc.readonly;\n          slots.readonlyUpdated.next(readonly);","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/blocksuite/affine/blocks/root/src/edgeless/edgeless-root-service.ts#L101-L137","documentation":"FrameworkStackProvider.getRaw probes each stacked provider with optional:true and returns the first truthy result; if none of them can build the component it throws ComponentNotFoundError for the stack as a whole. Because the probe uses truthiness, a factory that legitimately returns a falsy value (0, '', false, null) is also treated as 'not found' and the scan continues past it.","triggerScenarios":"Resolving an identifier that none of the stacked frameworks registered (e.g. the plugin framework that owns it was not composed in); a registered factory returning a falsy value that the stack scan skips; version skew where the framework expected to register the service is older.","commonSituations":"Plugin systems where a service lives in an optional framework that was not loaded; mocks replacing a factory with () => null; conditional registration behind flags leaving no stacked provider with the binding.","solutions":["Register the identifier in at least one of the stacked frameworks (usually the base one) before resolving through the stack","If factories may return falsy values, wrap them so they return objects (e.g. { value } or a boxed instance) — the truthiness probe cannot distinguish 'found but falsy'","If absence is expected, resolve with getOptional through the stack to receive undefined instead of the throw"],"exampleFix":"// before\nconst x = stackProvider.get(PluginService); // no stacked framework registered it\n\n// after\nbaseFramework.impl(PluginService, PluginServiceImpl); // register in one stacked framework\nconst x = stackProvider.get(PluginService);\n// or: const maybe = stackProvider.getOptional(PluginService);","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"import { ComponentNotFoundError } from '.../framework/core/error';\n\ntry {\n  const svc = stackProvider.get(PluginService);\n} catch (err) {\n  if (err instanceof ComponentNotFoundError) {\n    // none of the stacked frameworks provide it; register it or degrade\n  } else {\n    throw err;\n  }\n}","preventionTips":["Use getOptional() through the stack when a service may not be composed in","Register shared services in the base framework so the stack always finds them","Keep factories from returning falsy values (0, '', null, false) — the stack probe treats them as not-found"],"tags":["dependency-injection","provider-stack","resolution","not-registered"],"backgroundTag":"di-service-not-registered","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}