{"record":{"id":"a12dc64ba956431f","repo":"BabylonJS/Babylon.js","slug":"invalid-engine-unable-to-create-a-canvas-a12dc6","errorCode":null,"errorMessage":"Invalid engine. Unable to create a canvas.","messagePattern":"Invalid engine\\. Unable to create a canvas\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/gui/src/2D/controls/control.pure.ts","lineNumber":2830,"sourceCode":"\r\n    /** VERTICAL_ALIGNMENT_CENTER */\r\n    public static get VERTICAL_ALIGNMENT_CENTER(): number {\r\n        return Control._VERTICAL_ALIGNMENT_CENTER;\r\n    }\r\n\r\n    private static _FontHeightSizes: { [key: string]: { ascent: number; height: number; descent: number } } = {};\r\n\r\n    /**\r\n     * @internal\r\n     */\r\n    public static _GetFontOffset(font: string, engineToUse?: AbstractEngine): { ascent: number; height: number; descent: number } {\r\n        if (Control._FontHeightSizes[font]) {\r\n            return Control._FontHeightSizes[font];\r\n        }\r\n\r\n        const engine = engineToUse || EngineStore.LastCreatedEngine;\r\n        if (!engine) {\r\n            throw new Error(\"Invalid engine. Unable to create a canvas.\");\r\n        }\r\n\r\n        const result = engine.getFontOffset(font);\r\n        Control._FontHeightSizes[font] = result;\r\n\r\n        return result;\r\n    }\r\n\r\n    /**\r\n     * Creates a Control from parsed data\r\n     * @param serializedObject defines parsed data\r\n     * @param host defines the hosting AdvancedDynamicTexture\r\n     * @param urlRewriter defines an url rewriter to update urls before sending them to the controls\r\n     * @returns a new Control\r\n     */\r\n    public static Parse(serializedObject: any, host: AdvancedDynamicTexture, urlRewriter?: (url: string) => string): Control {\r\n        const controlType = Tools.Instantiate(\"BABYLON.GUI.\" + serializedObject.className);\r\n        const control = SerializationHelper.Parse(\r","sourceCodeStart":2812,"sourceCodeEnd":2848,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/gui/src/2D/controls/control.pure.ts#L2812-L2848","documentation":"Control._MeasureFontHeight (font-height caching helper) requires an engine to call engine.getFontOffset(font). It uses the passed engineToUse or falls back to EngineStore.LastCreatedEngine; when neither exists it throws, reusing the generic 'unable to create a canvas' message because font metrics depend on engine canvas facilities.","triggerScenarios":"Any Control text measurement (e.g. setting text, computing font height for TextBlock/Button) when no engine has been created, the engine was disposed, or the host/scene was built in a context without LastCreatedEngine — e.g. pure-mode usage without supplying engineToUse.","commonSituations":"Preloading or laying out GUI controls before engine init; SSR/Node rendering of GUI trees; tests constructing TextBlock with fonts; disposing the engine while GUI is still measuring on resize events.","solutions":["Create a Babylon engine (and scene) before instantiating/measuring text-bearing controls.","Pass an explicit engine via the API that accepts engineToUse instead of relying on LastCreatedEngine fallback.","Ensure the engine is not disposed before GUI layout completes; dispose GUI first.","In tests, seed EngineStore via a mock engine or avoid font measurement paths."],"exampleFix":"// before\nconst tb = new TextBlock(\"t\", \"hello\"); // measures font, throws if no engine\n\n// after\nconst engine = new Engine(canvas, true);\nconst scene = new Scene(engine);\nconst adt = AdvancedDynamicTexture.CreateFullscreenUI(\"ui\", true, scene);\nconst tb = new TextBlock(\"t\", \"hello\");\nadt.addControl(tb);","handlingStrategy":"validation","validationCode":"import { EngineStore } from \"@babylonjs/core/Engines/engineStore\";\nif (!EngineStore.LastCreatedEngine) {\n    throw new Error(\"Engine required before text controls can measure fonts\");\n}\nconst tb = new TextBlock(\"t\", \"hello\");","typeGuard":"function engineAvailable(e: unknown): e is { getFontOffset(font: string): { offset: number; height: number } } {\n    return !!e && typeof (e as any).getFontOffset === \"function\";\n}","tryCatchPattern":"try {\n    textBlock.text = \"hello\";\n} catch (e) {\n    if (e instanceof Error && e.message.includes(\"Invalid engine\")) {\n        console.error(\"Cannot measure font without an engine:\", e);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Initialize engine + scene before building any text-bearing controls (TextBlock, Button, InputTextBox).","Guard resize/layout handlers so they skip font measurement after engine.dispose().","Use APIs accepting an explicit engineToUse rather than the LastCreatedEngine fallback when possible.","In SSR/pure contexts, defer GUI construction until a real or mock engine exists."],"tags":["engine","canvas","text-measurement","gui","initialization-order"],"backgroundTag":"engine-not-initialized","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}